Search code examples
scalaimplicit-parameters

Error in lifting method to function


I have a method that with the implicit parameter. i get a error when i convert it to function in 2 case :

1:

def action(implicit i:Int) =  i + " in action"
val f = action _

then i get a StackOverflowError.

2:

def action(implicit i:Int) =  i + " in action"
val f = action(_)

then i get a error: missing parameter type

I must write like this :

val f = (i:Int) => action(i)

that's ok. And if the parameter of 'action' is not the implicit , all case are right. So how to explain , and what i miss ?


Solution

  • If you specify a parameter to a function to be implicit, you are inviting the compiler to supply the value of that parameter for you. So how does the compiler find those values? It looks for values of the same type (Int in your case) that have been declared as implicit values in a variety of scopes.

    (For simplicity, I'll just use a local scope in this example, but you might want to read up on this topic. Programming in Scala, 3rd Ed is a good first step.)

    Note that the names of the implicit values are ignored and have no bearing on proceedings, the compiler only looks at the types of implicit values. If multiple implicit values with the required type are found in the same scope, then the compiler will complain about ambiguous implicit values.

    For example, the following provides a function with an implicit parameter and a default value for that parameter within the current scope:

    def greetPerson(name: String)(implicit greeting: String) = s"$greeting $name!"
    implicit val defaultGreeting = "Hello" // Implicit value to be used for greeting argument.
    val y = greetPerson("Bob") // Equivalent to greetPerson("Bob")(defaultGreeting).
    val z = greetPerson("Fred")("Hi")
    

    Note that y is just a String value of "Hello Bob!", and z is a string with the value "Hi Fred!"; neither of them are functions.

    Also note that greetPerson is a curried function. This is because implicit parameters cannot be mixed with regular, non-implicit parameters in the same parameter list.

    In general, it's bad practice to use common types (Int, Boolean, String, etc.) as values for implicit parameters. In a big program, there might be a lot of different implicit values in your scope, and you might pick up an unexpected value. For that reason, it's standard practice to wrap such values in a case class instead.

    If you're trying to create a value that supplies some of the arguments of another function (that is, a partially applied function), then that would look something like this:

    def greetPerson(greeting: String, name: String) = s"$greeting $name!"
    val sayHello = greetPerson("Hello", _: String)
    val y = sayHello("Bob") // "Hello Bob!"
    val sayHi = greetPerson("Hi", _: String)
    val z = sayHi("Fred") // "Hi Fred!"
    

    In both cases, we're creating partially applied functions (sayHi and sayHello) that call greetPerson with the greeting parameter specified, but which allow us to specify the name parameter. Both sayHello and sayHi are still only values, but their values are partially applied functions rather than constants.

    Depending upon your circumstances, I think the latter case may suit you better...

    I would also read up on how the underscore character (_) is used in Scala. In a partially applied function declaration, it corresponds to the arguments that will be provided later. But it has a lot of other uses too. I think there's no alternative to reading up on Scala and learning how and when to use them.