Definiowanie funkcji z wieloma niejawnymi argumentami w Scali

95

Jak mogę zdefiniować funkcję z wieloma niejawnymi argumentami.

def myfun(arg:String)(implicit p1: String)(implicit p2:Int)={} // doesn't work
Ali Salehi
źródło
3
W tekście pytania pytasz o funkcję. We fragmencie kodu masz metodę. Pytasz o funkcję lub metodę?
Jörg W Mittag

Odpowiedzi:

190

Wszystkie muszą znajdować się na jednej liście parametrów, a ta lista musi być ostatnią.

def myfun(arg:String)(implicit p1: String, p2:Int)={} 
missingfaktor
źródło
1
Gdyby to była klasa, składnia byłaby klasą MyClass () (implicit p1: String, implicit p2: Int) {}
skjagini
2

W rzeczywistości istnieje sposób na zrobienie dokładnie tego, czego wymaga PO. Trochę zawiłe, ale działa.

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

W Scali 3 (aka „Dotty”, chociaż jest to nazwa kompilatora) zamiast zwracać pomocniczy obiekt MyFunPart2 , możliwe jest bezpośrednie zwrócenie wartości funkcji z niejawnymi argumentami. Dzieje się tak, ponieważ Scala 3 obsługuje „Niejawne funkcje” (tj. „Niejawność parametrów” jest teraz częścią typów funkcji). Wiele niejawnych list parametrów staje się tak łatwe do zaimplementowania, że ​​jest możliwe, że język będzie je obsługiwał bezpośrednio, chociaż nie jestem pewien.

Mario Rossi
źródło
1

Istnieje inny (prostszy i bardziej elastyczny IMO) sposób na osiągnięcie podobnego efektu:

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

Używanie krotki jako podstawowej reprezentacji parametrów nie jest dobrym pomysłem, ponieważ niejawne konwersje mogą kolidować z innymi zastosowaniami. W rzeczywistości niejawne konwersje na dowolny typ standardowy (w tym biblioteki) zwykle powodują problemy w każdej nietrywialnej aplikacji. Rozwiązaniem jest utworzenie dedykowanej klasy przypadku do przechowywania parametrów zamiast krotki. Ważną zaletą jest to, że można by nadać im nazwy znacznie bardziej znaczące niż _1 i _2.

Mario Rossi
źródło