Quelles sont les fonctionnalités cachées de Scala que chaque développeur de Scala devrait connaître ?
Un caché en vedette par réponse, s’il vous plaît.
Quelles sont les fonctionnalités cachées de Scala que chaque développeur de Scala devrait connaître ?
Un caché en vedette par réponse, s’il vous plaît.
Bon, j'ai dû ajouter un de plus. Chaque Regex
objet en Scala a un extracteur (voir la réponse de oxbox_lakes ci-dessus) qui vous donne accès à l'correspondent à des groupes. Si vous pouvez faire quelque chose comme:
// Regex to split a date in the format Y/M/D.
val regex = "(\\d+)/(\\d+)/(\\d+)".r
val regex(year, month, day) = "2010/1/13"
La deuxième ligne est source de confusion si vous n'êtes pas habitué à l'aide d'un patron et d'extracteurs. Chaque fois que vous définissez un val
ou var
, ce qui vient après le mot-clé n'est pas simplement un identifiant, mais plutôt un modèle. C'est pourquoi cela fonctionne:
val (a, b, c) = (1, 3.14159, "Hello, world")
La main droite expression crée un Tuple3[Int, Double, String]
qui peut correspondre à la forme (a, b, c)
.
La plupart du temps, vos habitudes d'utilisation des extracteurs qui sont membres de singleton objets. Par exemple, si vous écrivez un modèle de type
Some(value)
alors vous êtes implicitement appel de l'extracteur Some.unapply
.
Mais vous pouvez également utiliser des instances de classe dans des modèles, et c'est ce qui se passe ici. Le val regex est une instance de l' Regex
, et lorsque vous l'utilisez dans un motif, vous êtes implicitement appel regex.unapplySeq
(unapply
contre unapplySeq
est au-delà de la portée de cette réponse), dont des extraits de la correspondance de groupes en Seq[String]
, dont les éléments sont attribuées dans l'ordre pour les variables de l'année, le mois, et le jour.
Sans cette fonctionnalité, vous pouvez, par exemple, exprimer l'idée de la cartographie d'une fonction sur une liste de revenir une autre liste, ou la cartographie d'une fonction sur un arbre pour y retourner un autre arbre. Mais vous ne pouvez pas exprimer cette idée généralement sans plus sortes.
Avec plus de types, vous pouvez capturer l'idée de n'importe quel type qui est paramétré avec un autre type. Un constructeur de type qui prend un paramètre est dit d'être de nature (*->*)
. Par exemple, List
. Un constructeur de type, qui retourne un autre type constructeur est dit d'être de nature (*->*->*)
. Par exemple, Function1
. Mais en Scala, nous avons plus de sortes, afin que nous puissions avoir le type des constructeurs qui sont paramétrées avec d'autres type de constructeurs. Ils sont donc des types comme ((*->*)->*)
.
Par exemple:
trait Functor[F[_]] {
def fmap[A, B](f: A => B, fa: F[A]): F[B]
}
Maintenant, si vous avez un Functor[List]
, vous pouvez mapper sur les listes. Si vous avez un Functor[Tree]
, vous pouvez mapper sur les arbres. Mais plus important encore, si vous avez Functor[A]
pour toute Une de type (*->*)
, vous pouvez mapper une fonction sur A
.
Les extracteurs qui vous permettent de remplacer désordre if-elseif-else
code de style avec des motifs. Je sais que ce ne sont pas vraiment caché , mais j'ai été en utilisant Scala pour quelques mois, sans vraiment comprendre la puissance de la. Pour la (longue) exemple je peux remplacer:
val code: String = ...
val ps: ProductService = ...
var p: Product = null
if (code.endsWith("=")) {
p = ps.findCash(code.substring(0, 3)) //e.g. USD=, GBP= etc
}
else if (code.endsWith(".FWD")) {
//e.g. GBP20090625.FWD
p = ps.findForward(code.substring(0,3), code.substring(3, 9))
}
else {
p = ps.lookupProductByRic(code)
}
Avec cela, ce qui est beaucoup plus clair à mon avis
implicit val ps: ProductService = ...
val p = code match {
case SyntheticCodes.Cash(c) => c
case SyntheticCodes.Forward(f) => f
case _ => ps.lookupProductByRic(code)
}
J'ai du faire un peu de travail sur le terrain dans le fond...
object SyntheticCodes {
// Synthetic Code for a CashProduct
object Cash extends (CashProduct => String) {
def apply(p: CashProduct) = p.currency.name + "="
//EXTRACTOR
def unapply(s: String)(implicit ps: ProductService): Option[CashProduct] = {
if (s.endsWith("=")
Some(ps.findCash(s.substring(0,3)))
else None
}
}
//Synthetic Code for a ForwardProduct
object Forward extends (ForwardProduct => String) {
def apply(p: ForwardProduct) = p.currency.name + p.date.toString + ".FWD"
//EXTRACTOR
def unapply(s: String)(implicit ps: ProductService): Option[ForwardProduct] = {
if (s.endsWith(".FWD")
Some(ps.findForward(s.substring(0,3), s.substring(3, 9))
else None
}
}
Mais l'effort en vaut la peine pour le fait qu'il sépare un morceau de logique métier dans un endroit raisonnable. Je peux mettre en place mes Product.getCode
méthodes comme suit..
class CashProduct {
def getCode = SyntheticCodes.Cash(this)
}
class ForwardProduct {
def getCode = SyntheticCodes.Forward(this)
}
De manifestes qui sont une sorte de façon à obtenir les informations de type au moment de l’exécution, comme si la Scala avait réifié types.
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.