Definindo uma função com vários argumentos implícitos em Scala

94

Como posso definir uma função com vários argumentos implícitos.

def myfun(arg:String)(implicit p1: String)(implicit p2:Int)={} // doesn't work
Ali Salehi
fonte
2
No texto da pergunta, você está perguntando sobre uma função. Em seu snippet de código, você tem um método. Você está perguntando sobre uma função ou um método?
Jörg W Mittag

Respostas:

190

Todos eles devem ir em uma lista de parâmetros e esta lista deve ser a última.

def myfun(arg:String)(implicit p1: String, p2:Int)={} 
faltando faktor
fonte
1
Se fosse uma classe, a sintaxe seria class MyClass () (p1 implícito: String, p2 implícito: Int) {}
skjagini
2

Na verdade, existe uma maneira de fazer exatamente o que o OP requer. Um pouco complicado, mas 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

Em Scala 3 (também conhecido como "Dotty", embora este seja o nome do compilador) em vez de retornar um objeto auxiliar MyFunPart2 , é possível retornar um valor de função com argumentos implícitos diretamente. Isso ocorre porque o Scala 3 oferece suporte a "Funções implícitas" (ou seja, "implicitude de parâmetro" agora faz parte dos tipos de função). Várias listas de parâmetros implícitos se tornam tão fáceis de implementar que é possível que a linguagem as suporte diretamente, embora eu não tenha certeza.

Mario rossi
fonte
1

Existe outra maneira (IMO mais simples e mais flexível) de obter um efeito semelhante:

// 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 uma tupla como a representação subjacente para os parâmetros não é uma boa ideia porque as conversões implícitas podem interferir em outros usos. Na verdade, as conversões implícitas para qualquer tipo padrão (incluindo as de biblioteca) geralmente criam problemas em qualquer aplicativo não trivial. A solução é criar uma classe de caso dedicada para conter os parâmetros em vez de uma tupla. Uma vantagem importante é que eles podem receber nomes muito mais significativos do que _1 e _2.

Mario rossi
fonte