Description
In some cases it is necessary to abstract over context functions and ordinary functions.
For example, consider a function that would eliminate a dependency on a capability Foo
. We can write this in Scala 3 like this:
trait Foo
def provideContextFunction[A](f: Foo ?=> A): A =
f(using new Foo {})
def contextFunction(using Foo): Int =
42
provideContextFunction(contextFunction) // okay
However, we might also want to be able to eliminate an explicit dependency on a capability Foo
. This could be particular important for compatibility with Scala 2 where functions that depend on implicit parameters are ordinary functions since context functions do not exist.
For instance, this will not compile:
provideContextFunction(implicit foo => 42) // does not compile
We can do this explicitly:
def provideFunction[A](f: Foo => A): A =
f(new Foo {})
def function(foo: Foo): Int =
42
provideFunction(function) // okay
provideFunction(implicit foo => 42) // okay
However, we can't mix and match these:
// provideFunction(contextFunction) does not compile
// provideContextFunction(function) does not eliminate requirement
We also can't give provideFunction
and provideContextFunction
the same name since they erase to the same type.
This makes it impossible to write an operator that eliminates the dependency on a capability that can be used consistently across Scala 2 and Scala 3.
What we would like to be able to do is write something like:
def provide[A](f: FunctionLike[Foo, A]): A =
f.apply(new Foo {})
provide(contextFunction) // okay
provide(function) // okay
This is just pseudocode but assumes there is some common super type of functions and context functions that describes something that an argument can be applied to. It doesn't appear that this currently exists and apologies if I missed it.
Maybe there is another solution to this as well such as converting lambdas of the form implicit x => y
into context functions but it seems like Scala 2 is still going to be used commercially for a while so it will be important for library adoption of capabilities to be able to eliminate them in a way that works across Scala versions.