I'm trying to create an anonymous implementation of a context function in Scala 3. In detail, the code is the following:
def mem[E, A](block: Raise[E] ?=> A): Raise[E] ?=> A = new ContextFunction1[Raise[E], A] {
override def apply(using r: Raise[E]): A = block(using r)
}
However, if I try to compile it, the compiler gives me the following error:
[error] 213 |}
[error] | ^
[error] | Found: Object with ((in.rcard.raise4s.Raise[E]) ?=> A) {...}
[error] | Required: A
There is no error if I switch to a classic Function1
.
ContextFunction1
is not a part of Scala API https://docs.scala-lang.org/api/all.html, so shouldn't be used in source code manually, although it's documented at https://docs.scala-lang.org/scala3/reference/contextual/context-functions-spec.html
Even ordinary functions are normally instantiated via lambda syntax rather than new
keyword. new Function1
is compiled to invokespecial
while ... => ...
is compiled to invokedynamic
bytecode.
Define implicit functions via ?=>
(both on type and value level):
def mem[E, A](block: Raise[E] ?=> A): Raise[E] ?=> A = (r: Raise[E]) ?=> block(using r)
def mem[E, A](block: Raise[E] ?=> A): Raise[E] ?=> A = r ?=> block(using r)
def mem[E, A](block: Raise[E] ?=> A): Raise[E] ?=> A = _ ?=> block
def mem[E, A](block: Raise[E] ?=> A): Raise[E] ?=> A = block
In the compiler, symbols for ContextFunctionN
are created here:
/** The trait FunctionN and ContextFunctionN for some N
* @param name The name of the trait to be created
*
* FunctionN traits follow this template:
*
* trait FunctionN[-T0,...-T{N-1}, +R] extends Object {
* def apply($x0: T0, ..., $x{N_1}: T{N-1}): R
* }
*
* That is, they follow the template given for Function2..Function22 in the
* standard library, but without `tupled` and `curried` methods and without
* a `toString`.
*
* ContextFunctionN traits follow this template:
*
* trait ContextFunctionN[-T0,...,-T{N-1}, +R] extends Object {
* def apply(using $x0: T0, ..., $x{N_1}: T{N-1}): R
* }
* ImpureXYZFunctionN follow this template:
*
* type ImpureXYZFunctionN[-T0,...,-T{N-1}, +R] = {cap} XYZFunctionN[T0,...,T{N-1}, R]
*/
private def newFunctionNType(name: TypeName): Symbol = {
...
Being not able to instantiate ContextFunction1
seems to be expected behavior
// `ContextFunctionN` does not have constructors
!ctor.exists || zeroParams(ctor.info)
https://github.com/scala/scala3/blob/main/compiler/src/dotty/tools/dotc/core/Types.scala#L6011
/** ... check that ... no user-written class extends ContextFunctionN.
*/
def checkParents(cls: Symbol, parentTrees: List[Tree])(using Context): Unit = cls.info match {
...
https://github.com/scala/scala3/blob/main/compiler/src/dotty/tools/dotc/typer/RefChecks.scala#L120
For FunctionN
sources are generated for Scala 2 standard library (used in Scala 3 too). For ContextFunctionN
sources are not even generated.
At runtime ContextFunctionN
do not exist (Class.forName("scala.ContextFunction1")
throws ClassNotFoundException
). ContextFunctionN
are erased to FunctionN
.
The error message could be better. Such messages were shown often in early days of Dotty e.g. for polymorphic functions: Type lambda with higher kind