Defining a function with multiple implicit arguments in Scala

Scala

Scala Problem Overview


How can I define a function with multiple implicit arguments.

def myfun(arg:String)(implicit p1: String)(implicit p2:Int)={} // doesn't work

Scala Solutions


Solution 1 - Scala

They must all go in one parameter list, and this list must be the last one.

def myfun(arg:String)(implicit p1: String, p2:Int)={} 

Solution 2 - Scala

There actually is a way of doing exactly what the OP requires. A little convoluted, but it works.

class MyFunPart2(arg: String, /*Not implicit!*/ p1: String) {
  def apply(implicit p2: Int) = {
    println(arg+p1+p2)
    /* otherwise your actual code */
  }
}

def myFun(arg: String)(implicit p1: String): MyFunPart2= {
  new MyFunPart2(arg, p1)
}

implicit val iString= " world! "
implicit val iInt= 2019

myFun("Hello").apply
myFun("Hello")(" my friend! ").apply
myFun("Hello")(" my friend! ")(2020)

//  Output is:
//      Hello world! 2019
//      Hello my friend! 2019
//      Hello my friend! 2020

In Scala 3 (a.k.a. "Dotty", though this is the compiler's name) instead of returning an auxiliary MyFunPart2 object, it's possible to return a function value with implicit arguments directly. This is because Scala 3 supports "Implicit Functions" (i.e. "parameter implicitness" now is part of function types). Multiple implicit parameter lists become so easy to implement that it's possible the language will support them directly, though I'm not sure.

Solution 3 - Scala

There is another (IMO simpler and more flexible) way to achieve a similar effect:

// Note the implicit is now a Tuple2
def myFun(arg: String)(implicit p: (String, Int) ): Unit = {
  println(arg + p._1 + p._2)
  /*otherwise your actual code*/
}

// These implicit conversion are able to produce the basic implicit (String,Int) Tuples
implicit def idis(implicit is: String, ii: Int): (String,Int)= (is,ii)
implicit def idi(s: String)(implicit ii: Int): (String,Int)= (s,ii)

// The basic implicit values for both underlying parameters
implicit val iString = " world! "
implicit val iInt = 2019

myFun("Hello")
myFun("Hello")(" my friend! ")
myFun("Hello")(" my friend! ",2020)

// Output is:
//     Hello world! 2019
//     Hello my friend! 2019
//     Hello my friend! 2020

// If we add the following implicit, 
implicit def ids(i: Int)(implicit is: String)= (is,i)

// we can even do
myFun("Hello")(2020)

// , and output is:
//     Hello world! 2020

Using a Tuple as the underlying representation for the parameters is not a good idea because the implicit conversions could interfere with other uses. Actually, implicit conversions to any standard type (including library ones) usually create trouble in any non-trivial application. The solution is to create a dedicated case class to hold the parameters instead of a Tuple. An important advantage is that they could be given names much more meaningful than _1 and _2.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAli SalehiView Question on Stackoverflow
Solution 1 - ScalamissingfaktorView Answer on Stackoverflow
Solution 2 - ScalaMario RossiView Answer on Stackoverflow
Solution 3 - ScalaMario RossiView Answer on Stackoverflow