Why implicit parameter not working when combined with non implicit parameter
Asked Answered
W

2

5

A very simple use case, let's say I have a class Foo that is accepting 2 parameters 1 is normal parameter and 1 is implicit.

class Foo(val msg: String, implicit val n: Int) {
  def multiplier = msg * n
}

implicit val num: Int = 4
val foo = new Foo("yo")
println(foo.msg)

I know it will work if I move the implicit parameter another list i.e. curried class Foo(val msg: String)(implicit val n: Int). But lets say for some reason I don't want to do that.

Can someone explain why the current version of implementation is not working?

Wrongdoer answered 31/1, 2020 at 7:48 Comment(2)
Are you just curious, or do you have some situation where this would be useful for you?Negus
@suma I recently started working on a project/service (which is old) and it has this same pattern at many places in code which made me curious about it. It seems like implicit in this case isn't solving anything or doing anything.Wrongdoer
N
7

Language specification is written this way. You have to define in a separate parameter list. Language specification is not speaking about implicit parameters at all, only about an implicit parameter list:

Scala spec:

An implicit parameter list (implicit p1,…,pn) of a method marks the parameters p1,…,pn as implicit. A method or constructor can have only one implicit parameter list, and it must be the last parameter list given.

It might be possible to check if there is some reason in mailing list archives or other places.

Negus answered 31/1, 2020 at 7:57 Comment(0)
B
3

We cannot mix implicit and non-implicit formal parameters within a single parameter list because implicit modifier is a property of the list, not the parameter. Hence the following method definition is illegal

def foo(msg: String, implicit n: Int) = ???      // Error

however why then is the following legal

class Foo(val msg: String, implicit val n: Int)  // OK

Here it appears it is legal to mix implicit and non-implicit parameters within a single parameter clause when declaring a class constructor, however the implicit modifier does not apply to the formal parameter n but instead it is modifying corresponding automatically generated accessor member. Because we have declared n to be val this expands to something like

 class Foo {
   ...
   implicit def n(): Int = n;
   def <init>(msg: String, n: Int) = { // note how msg and n are *NOT* implicit
     ...
   }
 }

and it is perfectly legal to declare a member to be implicit. On the other hand, if we drop val declaration, thus not declaring a member, we see it does not compile

class Foo(val msg: String, implicit n: Int) // Error: 'val' expected but identifier found                                   ^

similarly to how it is illegal within a method definition. Therefore the reason why the following does not compile

implicit val num: Int = 4
new Foo("yo") // error: unspecified value parameter n

is because the formal parameter n is in fact not implicit.


As a side note, the following Scala 2 requirement

A method or constructor can have only one implicit parameter list, and it must be the last parameter list given.

has been altered by Scala 3 Multiple Given Clauses

There can be several given parameter clauses in a definition and given parameter clauses can be freely mixed with normal ones. Example:

def f(u: Universe)(given ctx: u.Context)(given s: ctx.Symbol, k: ctx.Kind) = ... 

where given corresponds to implicit.

Brainard answered 31/1, 2020 at 19:5 Comment(2)
"has been relaxed" If I understand it correctly, given still applies to a whole parameter list, it cannot be applied to a single parameter.Negus
@Negus Yes indeed it still applies to the whole parameter list. I meant relaxed in the sense it doesn't have to be last etc.Brainard

© 2022 - 2024 — McMap. All rights reserved.