-
Notifications
You must be signed in to change notification settings - Fork 1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can't Abstract Over Functions And Context Functions #15901
Comments
is this pattern reasonable? given FnToCtxFn[T, U]: Conversion[T => U, T ?=> U] with
def apply(f: T => U): T ?=> U =
t ?=> f(t) usage: scala> provideContextFunction(function.convert)
val res1: Int = 42 Edit: Its not so good for the anonymous function use-case |
@bishabosha Yes exactly: trait Foo
def provideFoo[A](f: Foo ?=> A): A =
f(using new Foo {})
given FnToCtxFn[T, U]: Conversion[T => U, T ?=> U] with
def apply(f: T => U): T ?=> U =
t ?=> f(t)
provideFoo(implicit foo => 42) // does not compile |
I guess you don't want this either: provideContextFunction((implicit (foo: Foo) => 42).convert) |
Yeah. The use of these contextual values is already less ergonomic in Scala 2 due to the need to have the provide((implicit (foo: Foo) => 42).convert) Versus: provide(implicit foo => 42) |
I tried doing it the other way around, making import scala.language.implicitConversions
trait Foo
def provideFoo[A](f: Foo => A): A =
f(new Foo {})
given FnToCtxFn[T, U]: Conversion[T ?=> U, T => U] with
def apply(f: T ?=> U): T => U =
t => f(using t)
def contextFunction(using Foo): Int =
42
def function(foo: Foo): Int =
42
provideFoo(implicit foo => 42) // okay
provideFoo(contextFunction) // okay
provideFoo(function) // okay
trait Bar
given Bar = new Bar {}
def moreComplexContextFunction(using Foo, Bar): Int =
42
provideFoo(moreComplexContextFunction) // does not compile |
If this is for cross compiling code, then with -source:3.0-migration the only change is to use an explicit type: // probably need to define elsewhere if cross compiled
implicit def FnToCtxFn[T, U]: Conversion[T => U, T ?=> U] = new Conversion {
def apply(f: T => U): T ?=> U =
t ?=> f(t)
}
provideContextFunction(implicit (foo: Foo) => 42) |
That is definitely a step in the right direction. However just having to specify the type parameter there is a significant downside. In one of the code bases I work on we have hundreds of uses of the equivalent of |
Actually I don't think that works. trait Foo
implicit def FnToCtxFn[T, U]: Conversion[T => U, T ?=> U] = new Conversion {
def apply(f: T => U): T ?=> U =
t ?=> f(t)
}
def provideContextFunction[A](f: Foo ?=> A): A =
f(using new Foo {})
def function(foo: Foo): Int =
42
provideContextFunction(function) |
If context functions are here to stay, and not just an experiment, then I would suggest that abstraction is critical to enable the sort of reuse that Scala excels at. |
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: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:
We can do this explicitly:
However, we can't mix and match these:
We also can't give
provideFunction
andprovideContextFunction
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:
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.The text was updated successfully, but these errors were encountered: