With normal lambdas I can talk about them in the runtime without invoking them
val f: (x: Int) => Int = x + 1
print(f == f) // true
print(f) // Playground$$$Lambda$103....
When I attempt this with a contextual function, Scala eagerly attempts to invoke f immediately when I am not wanting to do invocation yet.
val f: (x: Int) ?=> Int = x + 1
print(f == f) // No given instance of type Int
What mechanisms or tricks does Scala provide to talk about or inspect contextual lambdas in the runtime without causing the compiler to attempt to invoke them?
It’s not being invoked, this is just how given values work. The compiler will look for them and check them first. If a given instance is not there, then it will error. This is the case even if it’s not a lambda but just a normal def.
scala> def f(using n: Int) = n
def f(using n: Int): Int
scala> f
-- [E172] Type Error: -----------------------------------------------------------------
1 |f
| ^
| No given instance of type Int was found for parameter n of method f
1 error found
You see, a context lambda is just syntax sugar for writing this kind of def.
I suspect you are trying to use context functions without knowing them too well, in an unintended way. They are really not meant to be used like this.
This is quite a curiosity, i thought I’d be able to trick the compiler, but it looks like it does some wrapping at the call site so different references are created (this feels like a place that could be optimised though).
It does feel like an odd use case but i guess this is somewhat curious behaviour.
IMHO it’s not so unreasonable to want to be able pass around a context function as a value without immediately applying the context arguments. Isn’t that even the main point of having function values in a programming language?
The compiler will look for them and check them first.
it is doing this for the purposes of invoking the function. If it cannot find the required givens, it doesn’t have sufficient information to invoke, analogous to calling a function without giving it the required arguments.
def f(x: Int) = 3
f() // missing argument!
With the contextual function, look how f on it’s own evaluates to the return type Int when referenced
val f: (x: Int) ?=> Int = 2 + x
given Int = 3
f // 5
Rather than f evaluating to some Playground$$$Lambda$11... value in memory (like it would if this function had explicit parameters and we had not yet invoked it), it evaluates to 2 + 3, hence invocation.
It is the same behavior of a function with no parameters at all
def f = 3
f // 3
Which might pose a derivative question; how to avoid invoking a parameterless function? For example, I don’t want this to be true
def f = 3
def g = 3
f == g // true, but I wan't false
I want to compare the functions by memory, not value. Like how this would evaluate
val f = () => 3
val g = () => 3
f == g // false
f() == g() // true
val f: (x: Int) ?=> Int = 3 + x
var seq = Seq[(x: Int) ?=> Int]()
def first(seq: Seq[Any]) = seq(0)
seq = f +: seq
val a = first(seq)
seq = f +: seq
val b = first(seq)
a == b // false
Yeah, there is a similar problem with by-name parameters, you can’t just reference the thunk but the value produced by evaluating it.
I proposed for that to be fixed in Scala 3 but it was rejected in favour of keeping the syntax “lightweight”, which I am sure would be the same answer in this case.
While I understand your point, note that the problem here is that these are methods not functions. And you can’t compare methods, because they aren’t values.
Also, a parameterless method is by all intents and purposes the same as a value.
Again, not possible in this context, because this is a method, not a function.
There are no parameterless functions, only functions of empty parameters; e.g. () => A
So, I think that there is some wrapping happening here, and thus a new wrapper object is created when you re-add the function, thus the memory address you are comparing is of the wrapper, which is of course different.
BTW, if you want to compare by memory address use eq rather than == it is clearer and faster.
I think the best you can do to solve this and your other problems is create a custom type with an implicit conversion from context functions into it.
I will try to sketch it later.
I think the best you can do to solve this and your other problems is create a custom type with an implicit conversion from context functions into it.
This sounds excellent, if it’s possible. I similarly tried to get the context functions to implicitly convert into Some(...), but that broke the implicit argument resolution. Facing similar difficulties with your idea, I’ve tried
import scala.language.implicitConversions
class F(val f: (x: Int) ?=> Int)
case class Foo(id: Int):
var f: F = F(x + 1)
def modify(
f: F,
) =
this.f = f
given Conversion[(x: Int) ?=> Int, F] with
def apply(f: (x: Int) ?=> Int) = F(f)
val foo = Foo(0).modify(
f = x + 1
// Not found: x
)
There seems to be some weird issues with named tuples.
But, the main issue is now that the implicit conversion won’t kick it if the types are not known, but we actually wanted the implicit conversion to infer the types.