I am given two lambdas and I must determine if they are identical without invoking them. I attempt this
val a = (x: Int) => 1
val b = (x: Int) => 1
println(a == b)
but get false, seeming that Scala is comparing their memory identifiers rather than their literal definitions.
Is there any way to do this?
Note that my use case is slightly trickier – my lambdas have contextual parameters, and I must know if they are identical in a call-site where the Givens are not available; hence why I cannot invoke them.
e.g.
val a = (x: Int) ?=> 1
val b = (x: Int) ?=> 1
println(a == b) // scala complains about no implicits found
No, not to my knowledge. AFAIK you can only do that in more mathematical, dependently-typed languages / proof assistants, such as Lean by using “function extensionality”.
An approximation could be using Property based testing. This is not a guarantee that the functions are equal, but should give strong confidence.
You can create your own trait / typeclass for these lambdas if you know their type; and write a specialized equals that gives strong confidence using whatever method.
An intuitive way to see that this is at least the hardest possible problem, consider the following:
For most currently unsolved problems in math, there is a relatively simple program which has different behavior depending on whether the conjecture is true or not.
The simplest I know of is the 3 + 1 problem:
def collatz(x: Int): Unit =
if x == 1 then
return ()
else if x % 2 == 0 then // even branch
solve( x / 2 )
else // odd branch
solve( 3x + 1 )
val a = (x: Int) => solve(x)
val b = (x: Int) => ()
If a == b then the conjecture is true: every starting number eventually reaches 1 (in our program, returns ())
Otherwise, the conjecture is false: there exists an x such that collatz never reaches 1 (since the function must not return () on that input)
One concrete interpretation
It is possible you only wish to test the syntactic equality of functions:
val a = (x: Int) => 1
val b = (x: Int) => 0 + 1
myCustomTest(a, b) // returns false, since "1" is not the same as "0 + 1"
This is possible through reflection, but it would be hard and imperfect*, and it’s difficult to see the use for such a narrow definition of equivalence
*(x: Int) => 0+1 would be equal to (x: Int) =>/* hehe */ 0 + 1 since the whitespace and comments are not encoded into the reflection API (IIRC)
I have realized for my use case it is sufficient to know if the two lambdas are literally the same reference in memory, like you can do with normal lambdas
val f: (x: Int) => Int = x + 1
print(f == f) // true
That, f == f is my solution. I don’t need to check if two separate lambdas have identical definitions, I only need to know if they are the same instance.
So now my real problem is how to do this with contextual functions, since the compiler will attempt to invoke the functions immediately.
val f: (x: Int) ?=> Int = x + 1
print(f == f)
// No given instance of type Int
I need something with the semantics of this pseudo-code
println(@DontInvoke(f) == @DontInvoke(f)})
My intuition on how to solve this involve casting the lambda as Any to blind the compiler from seeing that it’s a contextual function and thus not attempting an invocation.
I know this is possible, but I am struggling to find cost-free ways to do it. Does scala not provide a simple way to juggle contextual functions around or talk about them without invoking them?
Made a standalone post since this question about the handling of contextual functions seems unique enough. Not just for comparison checks, but avoiding invocation them in general. Preventing invocation of contextual functions referenced
Because this is a function meant to update the first lambda with the new lambda only if they are different. Without invoking either yet.
moment 0: give foo a bomb (lambda)
moment 1: give foo a new bomb, but only update and replace if this new bomb is truly new.
moment …n: finally invoke whatever bomb is there.