Scala: Passing one implicit parameter implicitly and the other explicitly. Is it possible?
Asked Answered
M

5

40

Let's consider the function:

def foo(implicit a:Int, b:String) = println(a,b).

Now, let us assume that there is an implicit String and Int (implicit val i1=1) in scope but we want to pass an other, not implicit Int (val i2=2) explicitly to foo .

How can we do that ? Is it possible? Thanks for reading.

Montanez answered 21/3, 2014 at 7:43 Comment(1)
It would be nice if a future Scala version would support using named arguments in that case, e.g. implicit val a = 33; foo(b = "hello"). I can't think of a case where that would be problematic.Dactylography
P
38

All I can add is:

def foo(implicit a: Int, b: String) = println(a, b)
implicit val i1 = 1
implicit val s = ""
val i2 = 2
foo(i2, implicitly[String])
Parnassian answered 21/3, 2014 at 8:8 Comment(1)
And you know the crazy thing? You can actually even avoid to redundantly specify the argument type. In other words, the following compiles fine: foo(i2, implicitly), no need here to do foo(i2, implicitly[String])Trackless
P
7

In case your method has many implicit parameters (I sometimes have in my projects) and you sometimes want to just specify one of them explicit and let the others been resolved implicitly you can write implicitly for every other parameter like showed in my other answer. But sometimes you will change the signature of that method or the explicit parameter is in the middle of that parameter list, then you can make more readable client code with the following construct:

Suppose you have some types and their implicit dummy objects:

trait I1; implicit object I1 extends I1
trait I2; implicit object I2 extends I2
trait I3; implicit object I3 extends I3
trait I4; implicit object I4 extends I4
trait I5; implicit object I5 extends I5
trait I6; implicit object I6 extends I6

Now you have your method foo1 that uses these implicits:

def foo1(implicit i1: I1, i2: I2, i3: I3, i4: I4, i5: I5, i6: I6) {
  println(i1, i2, i3, i4, i5, i6)
}

Now you often want to explicitly specify i4: I4. So you write:

val i4 = new I4 {}
foo1(implicitly, implicitly, implicitly, i4, implicitly, implicitly)

Ugly!
With the following (should be placed in tight scope to method foo2 and perhaps renamed) wrapper for all implicits:

object Implicits {
  def apply(i4: I4)(implicit i1: I1, i2: I2, i3: I3, i5: I5, i6: I6) = new Implicits(i1, i2, i3, i4, i5, i6)
  implicit def applying(implicit i1: I1, i2: I2, i3: I3, i4: I4, i5: I5, i6: I6) = new Implicits(i1, i2, i3, i4, i5, i6)
}
class Implicits(val i1: I1, val i2: I2, val i3: I3, val i4: I4, val i5: I5, val i6: I6)

and the related method foo2:

def foo2(implicit implicits: Implicits) = {
  import implicits._
  println(i1, i2, i3, i4, i5, i6)
}

you can now call foo2 instead of foo1 the following way:

locally {
  foo2 // using implicit dummy objects I1, ..., I6 from above
  // or with explicit I4:
  val i4 = new I4 {}
  foo2(Implicits(i4))
}
Parnassian answered 21/3, 2014 at 14:33 Comment(0)
A
5
  1. Explicitly call foo(i2, s1) but you loose the benefit of the use of the implicit String
  2. Define def foo1(a: Int)(implicit b: String)=foo(a,b) and you call foo1(i2)
Abert answered 21/3, 2014 at 8:5 Comment(0)
F
3

You can create a new inner scope and define a new implicit val in it. The advantage is than when you have multiple function calls, this way you can override the one implicit for all of them in one place:

def foo(implicit a:Int, b:String) = println(a,b).

implicit val i = 1
implicit val s = ""

foo // call with original implicits

{
  implicit val i = 2

  foo // call with a new Int implicit

  foo // call with a new Int implicit again

}

Note: the new implicit must have the same variable name as the original one, so that it hides it, otherwise you will get a compiler error about ambiguous implicit values.

Fanion answered 10/3, 2017 at 12:33 Comment(0)
B
0

I know it's an old question but it still may be interesting. A nice way to do it is using implicitly as a default value:

scala> def foo(a: Int = implicitly[Int], b: String = implicitly[String]) = println(a,b)

scala> foo()
(10,boo)

scala> foo(50)
(50,boo)

scala> foo(b="bar")
(10,bar)
Bouquet answered 13/9, 2014 at 14:12 Comment(1)
Not really - in that case, the implicit parameter must be in scope at the method declaration site, instead of your normal behavior, at the method call site.Aquamanile

© 2022 - 2024 — McMap. All rights reserved.