Pourquoi ne pas l'option a une méthode fois?
-
26-10-2019 - |
Question
Je me demande pourquoi scala.Option
n'a pas de fold
méthode définie comme ceci:
fold(ifSome: A => B , ifNone: => B)
équivalent à
map(ifSome).getOrElse(ifNone)
est-il pas mieux que d'utiliser map
+ getOrElse
?
La solution
Vous pouvez faire:
opt foldLeft (els) ((x, y) => fun(x))
ou
(els /: opt) ((x,y) => fun(x))
(Les deux solutions évaluera els
par la valeur, ce qui pourrait ne pas être ce que vous voulez. Merci à Rex Kerr pour le pointant.)
Modifier
Mais ce que vous voulez vraiment est catamorphisme cata
(essentiellement un fold
qui non seulement poignées la valeur Some
mais aussi des cartes de la partie None
, qui est ce que vous avez décrit)
opt.cata(fun, els)
défini comme (où value
est la valeur d'option souteneur)
def cata[X](some: A => X, none: => X): X = value match {
case None => none
case Some(a) => some(a)
}
ce qui équivaut à opt.map(some).getOrElse(none)
.
Bien que je remarque que vous ne devez utiliser CATA quand il est la manière « plus naturelle » de l'exprimer. Il y a beaucoup de cas où un simple suffit map
-getOrElse
, surtout quand il implique potentiellement enchaînant beaucoup de map
s. (Bien que vous pouvez également enchaîner les fun
s avec la composition de fonction, bien sûr -. Cela dépend si vous voulez vous concentrer sur la composition de la fonction ou la transformation de la valeur)
Autres conseils
Personnellement, je trouve des méthodes telles que cata
qui prennent deux fermetures comme arguments exagérez souvent. Avez-vous vraiment gagner en lisibilité sur map
+ getOrElse
? Pensez à un nouveau venu à votre code: Que vont-ils faire de
opt cata { x => x + 1, 0 }
Pensez-vous vraiment c'est plus clair que
opt map { x => x + 1 } getOrElse 0
En fait, je dirais que ni est préférable à la bonne vieille
opt match {
case Some(x) => x + 1
case None => 0
}
Comme toujours, il y a une limite où l'abstraction supplémentaire ne donne pas les avantages que vous et se retourne contre-productif.
Il a finalement été ajouté à Scala 2.10 , avec la fold[B](ifEmpty: => B)(f: A => B): B
signature.
Malheureusement, cela a une conséquence négative commune: B
est déduit pour les appels basés uniquement sur l'argument ifEmpty
, qui est en pratique souvent plus étroite. Par exemple. (Une version correcte est déjà dans la bibliothèque standard, ceci est juste pour la démonstration)
def toList[A](x: Option[A]) = x.fold(Nil)(_ :: Nil)
Scala déduira B
être Nil.type
au lieu de List[A]
désiré et se plaignent de ne pas retourner f
Nil.type
. , Vous avez besoin au lieu un des
x.fold[List[A]](Nil)(_ :: Nil)
x.fold(Nil: List[A])(_ :: Nil)
Cela rend fold
pas tout à fait équivalent à correspondant match
.
Comme mentionné par Debilski, vous pouvez utiliser OptionW.cata
de Scalaz ou fold
. Jason a commenté, les paramètres nommés ce look agréable:
opt.fold { ifSome = _ + 1, ifNone = 0 }
Maintenant, si la valeur que vous voulez dans le cas de None
est mzero
pour certains Monoid[M]
et vous avez une f: A => M
fonction pour le cas Some
, vous pouvez faire ceci:
opt foldMap f
opt map (_ + 1) getOrElse 0
devient
opt foldMap (_ + 1)
Personnellement, je pense que Option
devrait avoir une méthode de apply
qui serait le catamorphisme. De cette façon, vous pouvez simplement faire ceci:
opt { _ + 1, 0 }
ou
opt { some = _ + 1, none = 0 }
En fait, ce serait bien d'avoir pour toutes les structures de données algébriques.