scalaimplicitscala-reflect

Creating syntactic sugar for a scala argument to give the illusion of a different type


I am creating a library and am exposing a class called A that has in it defined a function func(f: SomeType => A). It is imperative that func is called with the following syntax:

val x = new A()
val y = new A()
x.func(_ => y)

However in reality I want the body of func() to perform logic based on the value of y. The function being passed as argument is not actually important.

I'm new to Scala and I have looked into using implicits and scala reflect with little joy. How can I go about doing this?


Solution

  • This looks like a much better question than the one from the other day :)

    Let's discuss the type of _ => y. If your contract is such that the user must provide a function, and in reality they should provide one which totally ignores the argument and produces y, then you should probably make it Unit => A. That way, func body can just apply the received function to an empty value.

    Let's say that A = Int:

    val f: Unit => Int = _ =>  42
    
    def func(f: Unit => Int) = {
      f() // apply f to empty argument
    }
    
    func(f) // 42
    

    The only point of creating such a thunk would be to achieve lazy evaluation of y. Similar effect can be achieved by taking the call-by-name parameter (indicated by => Type) instead of a function:

    def func(f: => Int) = {
      f
    }
    
    func(42) // 42
    

    If you can't change the function type from SomeType => A, then you can obtain y by providing any kind of SomeType. Even null.

    Let's say that SomeType = String and A = Int:

    val f: String => Int = _ =>  42
    
    def func(f: String => Int) = {
      f(null) // ugly but works
    }
    
    func(f) // 42
    

    But keep in mind that this is extremely weird and misleading. If the function is not important, why is it being required? Someone could provide the following String => Int function:

    val f: String => Int = _.length
    

    And what you're saying is, "I don't care about the input String". Well, how are you going to obtain the Int then? If you decide to obtain the Int by passing some String value to f, which value are you going to pass?

    You would have to rely on the user of your library to always provide a function that doesn't care about the input argument, such as val f: String => Int = _ => 42 (as shown in an earlier example). Then you can obtain your Int by providing whatever random dummy String value, or even null, "knowing" (= hoping) that the provided function will ignore its argument.