149 votes

Fonctions cachées de la Scala

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.

85voto

Willis Blackburn Points 980

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.

51voto

oxbow_lakes Points 70013

Définitions de type structurel - c'est-à-dire un type décrit par quelles méthodes il prend en charge. Par exemple :

Notez que le type du paramètre n’est pas défini autre qu’il a une méthode

45voto

Apocalisp Points 22526

Type Constructeur de Polymorphisme (un.k.un. plus-kinded types)

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.

39voto

oxbow_lakes Points 70013

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)     
}

35voto

oxbow_lakes Points 70013

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.com

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.

Powered by:

X