Comment écrire une version d'argument variable paresseuse de «orElse»
-
22-07-2019 - |
Question
Est-il possible d'écrire une méthode généralisée ouElse
à partir de Option
qui prend un nombre variable d'arguments? C'est-à-dire au lieu de:
lazy val o1 = { println("foo"); None }
lazy val o2 = { println("bar"); Some("bar") }
lazy val o3 = { println("baz"); Some("baz") }
// ...
o1 orElse o2 orElse o3 // orElse ...
Vous pouvez utiliser:
orElse(o1, o2, o3) //, ...
La solution
Selon la Spécification du langage Scala (4.6 Déclarations de fonctions et Définitions) vous ne pouvez pas définir de paramètres varargs par nom:
ParamType ::= Type
| ‘=>’ Type
| Type ‘*’
scala> def orElse(x : (=> String)*)
<console>:1: error: no by-name parameter type allowed here
def orElse(x : (=> String)*)
Vous pouvez remplacer l'argument paresseux par une fonction et une conversion de type implicite:
def orElse[T](x : (()=> Option[T])*) : Option[T] =
if(x.isEmpty) None else x.first.apply.orElse(orElse((x drop 1) :_*))
implicit def anyToFun0[T](t : => T) : (() => T) = () => t
orElse(o1, o2, o3)
Autres conseils
J'ai trouvé la question un peu en retard :). Une possibilité consiste à envelopper = > Un
dans une classe d'assistance avec une fonction d'assistance pour simplifier sa création:
import scala.language.implicitConversions
class Helper[+A](value: => A) extends Function0[A] {
override def apply(): A = value;
}
object Helper {
def unapply[A](h: Helper[A]): Option[A] = Some(h());
}
implicit def toHelper[A](body: => A) = new Helper(body);
L’extracteur n’est pas requis, il permet simplement une correspondance facile avec l’aide. Ensuite, nous pouvons écrire
def orElse[A](xs: Helper[Option[A]]*): Option[A] =
xs.collectFirst[A]({
case Helper(Some(r)) => r;
})
lazy val o1 = { println("foo"); None }
lazy val o2 = { println("bar"); Some("bar") }
lazy val o3 = { println("baz"); Some("baz") }
orElse(o1, o2, o3) //, ...
Ceci est juste une solution simplifiée, une solution plus réaliste serait
def orElse[A](x: Option[A], xs: Helper[Option[A]]*): Option[A]
avec une mise en œuvre plus efficace.
Il existe déjà une classe similaire à Helper
dans Scalaz, appelée Nom
avec l'implémentation Besoin
qui garantit que le corps est évalué au maximum une fois. . Donc, avec Scalaz, il pourrait être mis en œuvre comme
import scala.language.implicitConversions
import scalaz._
import scalaz.Scalaz._
implicit def toNeed[A](body: => A): Name[A] = Need(body);
def orElse[A](xs: Name[Option[A]]*): Option[A] =
xs.collectFirst[A]({
case Name(Some(r)) => r;
})
lazy val o1 = { println("foo"); None }
lazy val o2 = { println("bar"); Some("bar") }
lazy val o3 = { println("baz"); Some("baz") }
orElse(o1, o2, o3) //, ...