Définition d'une fonction avec plusieurs arguments implicites dans Scala

94

Comment puis-je définir une fonction avec plusieurs arguments implicites.

def myfun(arg:String)(implicit p1: String)(implicit p2:Int)={} // doesn't work
Ali Salehi
la source
2
Dans le texte de la question, vous posez une question sur une fonction. Dans votre extrait de code, vous avez une méthode. Vous vous interrogez sur une fonction ou une méthode?
Jörg W Mittag

Réponses:

190

Ils doivent tous aller dans une seule liste de paramètres, et cette liste doit être la dernière.

def myfun(arg:String)(implicit p1: String, p2:Int)={} 
manquant
la source
1
S'il s'agissait d'une classe, la syntaxe serait la classe MyClass () (implicite p1: String, implicite p2: Int) {}
skjagini
2

Il existe en fait un moyen de faire exactement ce que le PO exige. Un peu alambiqué, mais ça marche.

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

Dans Scala 3 (alias "Dotty", bien que ce soit le nom du compilateur) au lieu de renvoyer un objet MyFunPart2 auxiliaire , il est possible de renvoyer directement une valeur de fonction avec des arguments implicites. Ceci est dû au fait que Scala 3 prend en charge les «fonctions implicites» (c'est-à-dire que le paramètre implicitness fait désormais partie des types de fonction). Plusieurs listes de paramètres implicites deviennent si faciles à implémenter qu'il est possible que le langage les supporte directement, bien que je ne sois pas sûr.

Mario Rossi
la source
1

Il existe un autre moyen (IMO plus simple et plus flexible) d'obtenir un effet similaire:

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

L'utilisation d'un Tuple comme représentation sous-jacente des paramètres n'est pas une bonne idée car les conversions implicites peuvent interférer avec d'autres utilisations. En fait, les conversions implicites vers n'importe quel type standard (y compris ceux de bibliothèque) créent généralement des problèmes dans toute application non triviale. La solution consiste à créer une classe de cas dédiée pour contenir les paramètres au lieu d'un Tuple. Un avantage important est qu'ils pourraient recevoir des noms beaucoup plus significatifs que _1 et _2.

Mario Rossi
la source