SLC: add into modifier to the Factory and BuildFrom traits

Motivation

IterableOnceOps#to is defined as:

def to[C1](factory: Factory[A, C1]): C1

xs.to(List), xs.to(Vector) etc. work today because the companion objects provide implicit conversions such as:

// scala.collection.IterableFactory
implicit def toFactory[A, CC[_]](factory: IterableFactory[CC]): Factory[A, CC[A]]

These are still old-style implicit defs. Once the standard library migrates its conversions to scala.Conversion, every use site of such a conversion will require import scala.language.implicitConversions — otherwise, it emits a feature warning that is scheduled to become an error.

The problem is already visible for user-defined factories written with the (recommended) Conversion:

object MyFactory

trait MyColl[A]

given [T] => Conversion[MyFactory.type, Factory[T, MyColl[T]]] = ???

List(1).to(MyFactory) // warning: Use of implicit conversion ... should be enabled

Requiring a language import for the everyday xs.to(factory) call is clearly unacceptable.

Proposal

Declare Factory and BuildFrom with the into soft modifier:

into trait Factory[-A, +C] extends Any {
...
}

into marks Factory as a valid implicit-conversion target, so a Conversion[X, Factory[…]] is applied at to‘s argument without a language import — at every call site (framework or user), without changing to‘s signature or any other method that takes a Factory. The same applies to BuildFrom if we choose to mark it as well.

This is precisely the scenario the into-as-a-modifier scheme was designed for: a small, fixed set of designated conversion-target types.

Alternative: per-call-site into

The call-site into would also work. It would be more safe, but (probably) requires more code changes at the user code (for all the Factory implicit parameters).
I don’t have a strong opinion which option is better.

import Conversion.into

def to[C1](factory: into[Factory[A, C1]]): C1

Out of scope

Another thing is the migration to Conversion. We cannot just replace the implicit def

implicit def toFactory[K, V, CC[_, _]](factory: MapFactory[CC]): Factory[(K, V), CC[K, V]] = new ToFactory[K, V, CC](factory)

with the Conversion

given toFactory: [K, V, CC[_, _]] => Conversion[MapFactory[CC], Factory[(K, V), CC[K, V]]] = new ToFactory[K, V, CC](_)

due to the binary compatibility. We may drop the implicit modifier and add a new given

def toFactory[K, V, CC[_, _]](factory: MapFactory[CC]): Factory[(K, V), CC[K, V]] = new ToFactory[K, V, CC](factory)
given [K, V, CC[_, _]] => Conversion[MapFactory[CC], Factory[(K, V), CC[K, V]]] = toFactory

but imo, it’s discussion for an another thread.

6 Likes

I agree with this, thanks for the writeup

1 Like

Looks like a no-brainer to me.

1 Like