Definición de una función con múltiples argumentos implícitos en Scala

94

¿Cómo puedo definir una función con múltiples argumentos implícitos?

def myfun(arg:String)(implicit p1: String)(implicit p2:Int)={} // doesn't work
Ali Salehi
fuente
2
En el texto de la pregunta, está preguntando sobre una función. En su fragmento de código, tiene un método. ¿Preguntas sobre una función o un método?
Jörg W Mittag

Respuestas:

190

Todos deben ir en una lista de parámetros, y esta lista debe ser la última.

def myfun(arg:String)(implicit p1: String, p2:Int)={} 
desaparecido faktor
fuente
1
Si fuera una clase, la sintaxis sería class MyClass () (p1 implícito: String, p2 implícito: Int) {}
skjagini
2

De hecho, hay una forma de hacer exactamente lo que requiere el OP. Un poco complicado, pero funciona.

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

En Scala 3 (también conocido como "Dotty", aunque este es el nombre del compilador) en lugar de devolver un objeto auxiliar MyFunPart2 , es posible devolver un valor de función con argumentos implícitos directamente. Esto se debe a que Scala 3 admite "Funciones implícitas" (es decir, "la implícitaidad de los parámetros" ahora forma parte de los tipos de funciones). Varias listas de parámetros implícitos se vuelven tan fáciles de implementar que es posible que el lenguaje las admita directamente, aunque no estoy seguro.

Mario Rossi
fuente
1

Hay otra forma (en mi opinión más simple y flexible) de lograr un efecto similar:

// 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

Usar una tupla como representación subyacente de los parámetros no es una buena idea porque las conversiones implícitas podrían interferir con otros usos. En realidad, las conversiones implícitas a cualquier tipo estándar (incluidas las de biblioteca) suelen crear problemas en cualquier aplicación no trivial. La solución es crear una clase de caso dedicada para contener los parámetros en lugar de una Tupla. Una ventaja importante es que se les podría dar nombres mucho más significativos que _1 y _2.

Mario Rossi
fuente