Fonctionnalités cachées de Scala

149

Quelles sont les fonctionnalités cachées de Scala dont chaque développeur Scala devrait être conscient?

Une fonction cachée par réponse, s'il vous plaît.

Krzysiek Goj
la source
6
Heh, cette question est aussi utile pour ses liens vers les autres articles de fonctionnalités cachées que pour la question elle-même. À votre santé!
JohnMetta
1
@mettadore regardez simplement les liens connexes sur le côté droit.
Daniel C. Sobral
2
@JohnMetta: Ou utilisez la balise .

Réponses:

85

D'accord, j'ai dû en ajouter un de plus. Chaque Regexobjet dans Scala a un extracteur (voir la réponse d'oxbox_lakes ci-dessus) qui vous donne accès aux groupes de correspondance. Vous pouvez donc 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 semble déroutante si vous n'êtes pas habitué à utiliser la correspondance de motifs et les extracteurs. Chaque fois que vous définissez un valou 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")

L'expression de droite crée un Tuple3[Int, Double, String]qui peut correspondre au modèle (a, b, c).

La plupart du temps, vos modèles utilisent des extracteurs qui sont membres d'objets singleton. Par exemple, si vous écrivez un modèle comme

Some(value)

alors vous appelez implicitement 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 Regex, et lorsque vous l'utilisez dans un modèle, vous appelez implicitement regex.unapplySeq( unapplypar opposition à ce qui unapplySeqdépasse la portée de cette réponse), ce qui extrait les groupes de correspondance en a Seq[String], dont les éléments sont affectés afin de les variables année, mois et jour.

Willis Blackburn
la source
1
Merci d'avoir publié ça! Pour info, il est mentionné dans le chapitre "Extraire avec des expressions régulières" dans le livre "Programming in Scala" à la page 503 dans la première édition et à la page 611 dans la deuxième édition.
earthling paul
51

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

object Closer {
    def using(closeable: { def close(): Unit }, f: => Unit) {
      try { 
        f
      } finally { closeable.close }
    }
}

Notez que le type du paramètre closeablen'est défini que s'il a une closeméthode

oxbow_lakes
la source
1
Les types structurels ne sont même pas mentionnés dans "Programming in Scala". Elles sont un peu plus lentes que les autres techniques pour passer des types car elles utilisent la réflexion pour appeler les bonnes méthodes. (J'espère qu'ils trouveront un moyen d'accélérer cela.)
Ken Bloom
1
Et il est également possible de créer un alias pour eux, ce qui fonctionne comme une interface affectée en externe (très lente): type Closeable = {def close (): Unit}
Alexey
45

Polymorphisme type-constructeur (aka types de type supérieur)

Sans cette fonctionnalité, vous pouvez, par exemple, exprimer l'idée de mapper une fonction sur une liste pour renvoyer une autre liste, ou de mapper une fonction sur un arbre pour renvoyer un autre arbre. Mais vous ne pouvez pas exprimer cette idée de manière générale sans types supérieurs.

Avec les types supérieurs, vous pouvez capturer l'idée de tout type paramétré avec un autre type. Un constructeur de type qui prend un paramètre est dit de type (*->*). Par exemple List,. Un constructeur de type qui renvoie un autre constructeur de type est dit de type (*->*->*). Par exemple Function1,. Mais dans Scala, nous avons des types supérieurs , nous pouvons donc avoir des constructeurs de type paramétrés avec d'autres constructeurs de type. Donc, ils sont du genre ((*->*)->*).

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 des listes. Si vous avez un Functor[Tree], vous pouvez cartographier les arbres. Mais plus important encore, si vous avez Functor[A] pour tout type A(*->*) , vous pouvez mapper une fonction A.

Apocalisp
la source
39

Extracteurs qui vous permettent de remplacer le if-elseif-elsecode de style désordonné par des modèles. Je sais que ce ne sont pas exactement cachés mais j'utilise Scala depuis quelques mois sans vraiment en comprendre la puissance. Pour un (long) 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)
}

Je dois faire un peu de travail en arrière-plan ...

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 les démarches en valent la peine, car elles séparent un élément de logique métier en un endroit sensible. Je peux implémenter mes Product.getCodeméthodes comme suit.

class CashProduct {
  def getCode = SyntheticCodes.Cash(this)
}

class ForwardProduct {
  def getCode = SyntheticCodes.Forward(this)     
}
oxbow_lakes
la source
n'est-ce pas comme un interrupteur? peut-être que cela pourrait être remanié davantage.
Geo
14
Les modèles sont comme des commutateurs turbo-chargés: beaucoup plus puissants et clairs
oxbow_lakes
1
Bien, mais je n'aime pas que vous deviez utiliser implicite car sa portée va plus loin que la correspondance {}. Vous pouvez également simplement ajouter une méthode à ProductService qui recherche un produit par code. Vous envelopperiez votre extrait refactorisé dans une méthode de toute façon pour pouvoir l'utiliser partout.
Martin Konicek
35

Manifeste qui est une sorte de moyen d'obtenir les informations de type au moment de l'exécution, comme si Scala avait réifié des types.

oxbow_lakes
la source
8
Je pense qu'il est préférable d'expliquer la réponse dans la réponse plutôt que de faire référence à un lien. Au fait, salut agai oxbow! :-)
Daniel C. Sobral
C'est une fonctionnalité vraiment cachée ... même pas dans la documentation de l'API. Très utile cependant.
André Laszlo
35

Dans scala 2.8, vous pouvez avoir des méthodes récursives de fin en utilisant le package scala.util.control.TailCalls (en fait, il s'agit de trampoline).

Un exemple:

def u(n:Int):TailRec[Int] = {
  if (n==0) done(1)
  else tailcall(v(n/2))
}
def v(n:Int):TailRec[Int] = {
  if (n==0) done(5)
  else tailcall(u(n-1))
}
val l=for(n<-0 to 5) yield (n,u(n).result,v(n).result)
println(l)
Aymen
la source
35

Les classes de cas se mélangent automatiquement dans le trait Product, fournissant un accès non typé et indexé aux champs sans aucune réflexion:

case class Person(name: String, age: Int)

val p = Person("Aaron", 28)
val name = p.productElement(0) // name = "Aaron": Any
val age = p.productElement(1) // age = 28: Any
val fields = p.productIterator.toList // fields = List[Any]("Aaron", 28)

Cette fonctionnalité fournit également un moyen simplifié de modifier la sortie de la toStringméthode:

case class Person(name: String, age: Int) {
   override def productPrefix = "person: "
}

// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28)) 
Aaron Novstrup
la source
32

Ce n'est pas exactement caché, mais certainement une fonctionnalité sous-annoncée: scalac -Xprint .

Pour illustrer l'utilisation, considérons la source suivante:

class A { "xx".r }

Compiler cela avec scalac -Xprint: sorties typer :

package <empty> {
  class A extends java.lang.Object with ScalaObject {
    def this(): A = {
      A.super.this();
      ()
    };
    scala.this.Predef.augmentString("xx").r
  }
}

Remarquez scala.this.Predef.augmentString("xx").r, qui est une application du implicit def augmentStringprésent dans Predef.scala.

scalac -Xprint: <phase> affichera l'arbre de syntaxe après une phase de compilation. Pour voir les phases disponibles, utilisez scalac -Xshow-phases .

C'est un excellent moyen d'apprendre ce qui se passe dans les coulisses.

Essayez avec

case class X(a:Int,b:String)

en utilisant la phase de frappe pour vraiment sentir à quel point c'est utile.

pedrofurla
la source
30

Vous pouvez définir vos propres structures de contrôle. Ce ne sont vraiment que des fonctions et des objets et du sucre syntaxique, mais ils ressemblent et se comportent comme la vraie chose.

Par exemple, le code suivant définit dont {...} unless (cond)et dont {...} until (cond):

def dont(code: => Unit) = new DontCommand(code)

class DontCommand(code: => Unit) {
  def unless(condition: => Boolean) =
    if (condition) code

  def until(condition: => Boolean) = {
    while (!condition) {}
    code
  }
}

Vous pouvez maintenant effectuer les opérations suivantes:

/* This will only get executed if the condition is true */
dont {
  println("Yep, 2 really is greater than 1.")
} unless (2 > 1) 

/* Just a helper function */
var number = 0;
def nextNumber() = {
  number += 1
  println(number)
  number
}

/* This will not be printed until the condition is met. */
dont {
  println("Done counting to 5!")
} until (nextNumber() == 5) 
Aleksander Kmetec
la source
Quelques autres exemples ici: programmers.stackexchange.com/questions/13072/…
missingfaktor
Je serais curieux de savoir si quelqu'un connaît un moyen de définir des blocs if-then-else avec un autre optionnel qui vérifie le type comme les blocs standard.
Philippe
@Philippe: zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero. Nécessite Scalaz.
missingfaktor
26

@switch annotation dans Scala 2.8:

Une annotation à appliquer à une expression de correspondance. S'il est présent, le compilateur vérifiera que la correspondance a été compilée vers un commutateur de table ou un commutateur de recherche, et émettra une erreur s'il se compile à la place en une série d'expressions conditionnelles.

Exemple:

scala> val n = 3
n: Int = 3

scala> import annotation.switch
import annotation.switch

scala> val s = (n: @switch) match {
     |   case 3 => "Three"
     |   case _ => "NoThree"
     | }
<console>:6: error: could not emit switch for @switch annotated match
       val s = (n: @switch) match {
manquants
la source
26

Je ne sais pas si cela est vraiment caché, mais je trouve cela plutôt sympa.

Les constructeurs de types qui prennent 2 paramètres de type peuvent être écrits en notation infixe

object Main {                                                                   
  class FooBar[A, B]

  def main(args: Array[String]): Unit = {
    var x: FooBar[Int, BigInt] = null
    var y: Int FooBar BigInt   = null
  }
}
Raichoo
la source
1
Agréable! J'imagine que cela est parfois utile pour améliorer la lisibilité. Par exemple var foo2barConverter: Foo ConvertTo Bar, l'ordre des paramètres de type est évident.
Esko Luontola
4
Je fais parfois cela dans du code qui utilise PartialFunction dans une certaine mesure: tapez ~> [A, B] = PartialFunction [A, B]
raichoo
24

Scala 2.8 a introduit des arguments par défaut et nommés, ce qui a rendu possible l'ajout d'une nouvelle méthode "copie" que Scala ajoute aux classes de cas. Si vous définissez ceci:

case class Foo(a: Int, b: Int, c: Int, ... z:Int)

et vous voulez créer un nouveau Foo qui ressemble à un Foo existant, uniquement avec une valeur "n" différente, alors vous pouvez simplement dire:

foo.copy(n = 3)
Willis Blackburn
la source
3
ATTENTION: la méthode de copie ne sera pas remplacée si vous héritez d'une classe de cas d'une autre. Vous devez donc le remplacer manuellement
Alexey
En relation: Un moyen plus propre de mettre à jour les structures imbriquées stackoverflow.com/q/3900307/203968
oluies
5
la classe de cas n'est plus autorisée (Scala 2.8) à hériter d'une classe de cas. Merci seigneur de Scala d'avoir désapprouvé cet héritage impie.
olle kullberg
24

dans scala 2.8, vous pouvez ajouter @specialized à vos classes / méthodes génériques. Cela créera des versions spéciales de la classe pour les types primitifs (étendant AnyVal) et économisera le coût d'un boxing / unboxing non nécessaire: class Foo[@specialized T]...

Vous pouvez sélectionner un sous-ensemble d'AnyVals: class Foo[@specialized(Int,Boolean) T]...

Aymen
la source
1
Y a-t-il une explication plus longue que vous pourriez m'indiquer? J'aimerais en savoir plus.
Paweł Prażak
23

Extension de la langue. J'ai toujours voulu faire quelque chose comme ça en Java (impossible). Mais dans Scala, je peux avoir:

  def timed[T](thunk: => T) = {
    val t1 = System.nanoTime
    val ret = thunk
    val time = System.nanoTime - t1
    println("Executed in: " + time/1000000.0 + " millisec")
    ret
  }

puis écrivez:

val numbers = List(12, 42, 3, 11, 6, 3, 77, 44)
val sorted = timed {   // "timed" is a new "keyword"!
  numbers.sortWith(_<_)
}
println(sorted)

et obtenir

Executed in: 6.410311 millisec
List(3, 3, 6, 11, 12, 42, 44, 77)
Adrian
la source
23

Vous pouvez désigner un paramètre call-by-name (EDITED: c'est différent d'un paramètre paresseux!) À une fonction et il ne sera évalué qu'après utilisation par la fonction (EDIT: en fait, il sera réévalué à chaque fois utilisé). Consultez cette FAQ pour plus de détails

class Bar(i:Int) {
    println("constructing bar " + i)
    override def toString():String = {
        "bar with value: " + i
    }
}

// NOTE the => in the method declaration.  It indicates a lazy paramter
def foo(x: => Bar) = {
    println("foo called")
    println("bar: " + x)
}


foo(new Bar(22))

/*
prints the following:
foo called
constructing bar 22
bar with value: 22
*/
agilefall
la source
Je pensais que "x: => Bar" signifiait que x était une fonction qui ne prenait aucun paramètre et renvoyait une barre. Ainsi, "new bar (22)" est juste une fonction anonyme, et est évaluée comme une fonction comme toute autre fonction.
Alex Black
1
"x: () => Bar" définit une fonction xa qui ne prend aucun paramètre et renvoie une barre. x: => Bar définit x comme un appel par nom. Jetez un œil à scala.sygneca.com/faqs/… pour plus de détails
agilefall
3
Ce que vous affichez, ce sont les paramètres d'appel par nom. Les paramètres paresseux ne sont pas encore implémentés: lampesvn.epfl.ch/trac/scala/ticket/240
ArtemGr
Je pense que vous pouvez l'utiliser comme paramètre paresseux si vous faites quelque chose comme lazy val xx: Bar = xdans votre méthode et à partir de ce moment, vous n'utilisez que xx.
Cristian Vrabie
20

Vous pouvez utiliser locallypour introduire un bloc local sans causer de problèmes d'inférence de point-virgule.

Usage:

scala> case class Dog(name: String) {
     |   def bark() {
     |     println("Bow Vow")
     |   }
     | }
defined class Dog

scala> val d = Dog("Barnie")
d: Dog = Dog(Barnie)

scala> locally {
     |   import d._
     |   bark()
     |   bark()
     | }
Bow Vow
Bow Vow

locally est défini dans "Predef.scala" comme:

@inline def locally[T](x: T): T = x

Étant en ligne, il n'impose aucune surcharge supplémentaire.

manquant
la source
3
Ceci est mieux expliqué sur stackoverflow.com/questions/3237727/...
Esko Luontola le
17

Initialisation précoce:

trait AbstractT2 {
  println("In AbstractT2:")
  val value: Int
  val inverse = 1.0/value
  println("AbstractT2: value = "+value+", inverse = "+inverse)
}

val c2c = new {
  // Only initializations are allowed in pre-init. blocks.
  // println("In c2c:")
  val value = 10
} with AbstractT2

println("c2c.value = "+c2c.value+", inverse = "+c2c.inverse)

Production:

In AbstractT2:  
AbstractT2: value = 10, inverse = 0.1  
c2c.value = 10, inverse = 0.1

Nous instancions une classe interne anonyme, initialisant le valuechamp dans le bloc, avant la with AbstractT2clause. Cela garantit qu'il valueest initialisé avant que le corps de AbstractT2soit exécuté, comme indiqué lorsque vous exécutez le script.

90%
la source
1
La construction est appelée «initialisation précoce».
Randall Schulz
17

Vous pouvez composer des types structurels avec le mot-clé "avec"

object Main {
  type A = {def foo: Unit}
  type B = {def bar: Unit}

  type C = A with B

  class myA {
    def foo: Unit = println("myA.foo")
  }


  class myB {
    def bar: Unit = println("myB.bar")
  }
  class myC extends myB {
    def foo: Unit = println("myC.foo")
  }

  def main(args: Array[String]): Unit = { 
    val a: A = new myA 
    a.foo
    val b: C = new myC 
    b.bar
    b.foo
  }
}
Raichoo
la source
17

syntaxe d'espace réservé pour les fonctions anonymes

À partir de la spécification du langage Scala:

SimpleExpr1 ::= '_'

Une expression (de catégorie syntaxique Expr) peut contenir des symboles de soulignement incorporés _aux endroits où les identificateurs sont légaux. Une telle expression représente une fonction anonyme où les occurrences ultérieures de traits de soulignement dénotent des paramètres successifs.

À partir des changements de langue Scala :

_ + 1                  x => x + 1
_ * _                  (x1, x2) => x1 * x2
(_: Int) * 2           (x: Int) => x * 2
if (_) x else y        z => if (z) x else y
_.map(f)               x => x.map(f)
_.map(_ + 1)           x => x.map(y => y + 1)

En utilisant cela, vous pouvez faire quelque chose comme:

def filesEnding(query: String) =
  filesMatching(_.endsWith(query))
Eugene Yokota
la source
2
Cela devrait être appelé «syntaxe d'espace réservé pour les fonctions anonymes». Implicite a une signification distincte dans Scala, et ce n'est pas lié à cela.
retronym le
Le lien a une relation non évidente avec la réponse. «implicite» n'est pas le terme correct pour cela. Comme ci-dessus, il devrait être «espace réservé».
Alain O'Dea
2
Ce n'est pas vraiment "caché", j'ai vu cet usage dans presque tous les tutoriels sur Scala que j'ai lus ... :-) Mais j'apprécie la définition formelle que je n'ai pas encore vue.
PhiLho
@PhiLho était peut-être moins connu en 2009. Je ne sais pas.
Eugene Yokota
J'ai manqué la date d'origine, car seule la dernière date de modification est affichée. Et bien, toutes les fonctionnalités expliquées dans ce fil ne sont pas "cachées". Fil cool et bonne réponse de toute façon.
PhiLho
16

Définitions implicites, en particulier les conversions.

Par exemple, supposons une fonction qui formatera une chaîne d'entrée pour l'adapter à une taille, en remplaçant le milieu de celle-ci par "...":

def sizeBoundedString(s: String, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Vous pouvez l'utiliser avec n'importe quelle chaîne et, bien sûr, utiliser la méthode toString pour convertir n'importe quoi. Mais vous pouvez aussi l'écrire comme ceci:

def sizeBoundedString[T](s: T, n: Int)(implicit toStr: T => String): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Et puis, vous pouvez passer des classes d'autres types en faisant ceci:

implicit def double2String(d: Double) = d.toString

Vous pouvez maintenant appeler cette fonction en passant un double:

sizeBoundedString(12345.12345D, 8)

Le dernier argument est implicite et est passé automatiquement en raison de la déclaration implicite de. En outre, "s" est traité comme une chaîne à l'intérieur de sizeBoundedString car il y a une conversion implicite de celui-ci en String.

Les implicits de ce type sont mieux définis pour les types inhabituels afin d'éviter les conversions inattendues. Vous pouvez également passer explicitement une conversion, et elle sera toujours implicitement utilisée dans sizeBoundedString:

sizeBoundedString(1234567890L, 8)((l : Long) => l.toString)

Vous pouvez également avoir plusieurs arguments implicites, mais vous devez alors les passer tous ou ne pas les transmettre. Il existe également une syntaxe de raccourci pour les conversions implicites:

def sizeBoundedString[T <% String](s: T, n: Int): String = {
  if (n < 5 && n < s.length) throw new IllegalArgumentException
  if (s.length > n) {
    val trailLength = ((n - 3) / 2) min 3
    val headLength = n - 3 - trailLength
    s.substring(0, headLength)+"..."+s.substring(s.length - trailLength, s.length)
  } else s
}

Ceci est utilisé exactement de la même manière.

Les implicits peuvent avoir n'importe quelle valeur. Ils peuvent être utilisés, par exemple, pour masquer les informations de la bibliothèque. Prenons l'exemple suivant, par exemple:

case class Daemon(name: String) {
  def log(msg: String) = println(name+": "+msg)
}

object DefaultDaemon extends Daemon("Default")

trait Logger {
  private var logd: Option[Daemon] = None
  implicit def daemon: Daemon = logd getOrElse DefaultDaemon

  def logTo(daemon: Daemon) = 
    if (logd == None) logd = Some(daemon) 
    else throw new IllegalArgumentException

  def log(msg: String)(implicit daemon: Daemon) = daemon.log(msg)
}

class X extends Logger {
  logTo(Daemon("X Daemon"))

  def f = {
    log("f called")
    println("Stuff")
  }

  def g = {
    log("g called")(DefaultDaemon)
  }
}

class Y extends Logger {
  def f = {
    log("f called")
    println("Stuff")
  }
}

Dans cet exemple, appeler "f" dans un objet Y enverra le journal au démon par défaut, et sur une instance de X au démon Daemon X. Mais appeler g sur une instance de X enverra le journal au DefaultDaemon explicitement donné.

Bien que cet exemple simple puisse être réécrit avec une surcharge et un état privé, les implicits ne nécessitent pas d'état privé et peuvent être mis en contexte avec les importations.

Daniel C. Sobral
la source
13

Peut-être pas trop caché, mais je pense que c'est utile:

@scala.reflect.BeanProperty
var firstName:String = _

Cela générera automatiquement un getter et un setter pour le champ qui correspond à la convention du bean.

Description supplémentaire chez developerworks

agilefall
la source
6
Et vous pouvez créer un raccourci pour cela si vous l'utilisez beaucoup, par exemple: import scala.reflect. {BeanProperty => BP}
Alexey
13

Arguments implicites dans les fermetures.

Un argument de fonction peut être marqué comme implicite comme avec les méthodes. Dans le cadre du corps de la fonction, le paramètre implicite est visible et éligible pour une résolution implicite:

trait Foo { def bar }

trait Base {
  def callBar(implicit foo: Foo) = foo.bar
}

object Test extends Base {
  val f: Foo => Unit = { implicit foo =>
    callBar
  }
  def test = f(new Foo {
    def bar = println("Hello")
  })
}
axel22
la source
12

Les types de résultats dépendent de la résolution implicite. Cela peut vous donner une forme d'envoi multiple:

scala> trait PerformFunc[A,B] { def perform(a : A) : B }
defined trait PerformFunc

scala> implicit val stringToInt = new PerformFunc[String,Int] {
  def perform(a : String)  = 5
}
stringToInt: java.lang.Object with PerformFunc[String,Int] = $anon$1@13ccf137

scala> implicit val intToDouble = new PerformFunc[Int,Double] {
  def perform(a : Int) = 1.0
}
intToDouble: java.lang.Object with PerformFunc[Int,Double] = $anon$1@74e551a4

scala> def foo[A, B](x : A)(implicit z : PerformFunc[A,B]) : B = z.perform(x)
foo: [A,B](x: A)(implicit z: PerformFunc[A,B])B

scala> foo("HAI")
res16: Int = 5

scala> foo(1)
res17: Double = 1.0
jsuereth
la source
Cela pourrait être le cas, mais la session ci-dessus est trompeuse. La définition des foousages et aqui doivent avoir été présents dans l'environnement avant l'exécution de ces commandes. Je suppose que vous vouliez dire z.perform(x).
Daniel C.Sobral
4

L'équivalent de Scala de l'initialiseur Java à double accolade.

Scala vous permet de créer une sous-classe anonyme avec le corps de la classe (le constructeur) contenant des instructions pour initialiser l'instance de cette classe.

Ce modèle est très utile lors de la construction d'interfaces utilisateur basées sur des composants (par exemple Swing, Vaadin) car il permet de créer des composants d'interface utilisateur et de déclarer leurs propriétés de manière plus concise.

Voir http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf pour plus d'informations.

Voici un exemple de création d'un bouton Vaadin:

val button = new Button("Click me"){
 setWidth("20px")
 setDescription("Click on this")
 setIcon(new ThemeResource("icons/ok.png"))
}
Guillaume Belrose
la source
3

Exclure des membres des importdéclarations

Supposons que vous souhaitiez utiliser une Loggerqui contient une méthode printlnet une printerr, mais que vous ne souhaitiez utiliser que celle pour les messages d'erreur et conserver la bonne vieille Predef.printlnpour la sortie standard. Vous pouvez faire ceci:

val logger = new Logger(...)
import logger.printerr

mais si loggercontient également douze autres méthodes que vous souhaitez importer et utiliser, il devient peu pratique de les lister. Vous pouvez à la place essayer:

import logger.{println => donotuseprintlnt, _}

mais cela «pollue» toujours la liste des membres importés. Entrez le joker über-puissant:

import logger.{println => _, _}

et cela fera ce qu'il faut ™.

Philippe
la source
2

requireméthode (définie dans Predef) qui vous permet de définir des contraintes de fonction supplémentaires qui seraient vérifiées lors de l'exécution. Imaginez que vous développiez un autre client Twitter et que vous deviez limiter la longueur des tweets à 140 symboles. De plus, vous ne pouvez pas publier de tweet vide.

def post(tweet: String) = {
  require(tweet.length < 140 && tweet.length > 0) 
  println(tweet)
 }

Maintenant, appeler post avec un argument de longueur inapproprié provoquera une exception:

scala> post("that's ok")
that's ok

scala> post("")
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

scala> post("way to looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong tweet") 
java.lang.IllegalArgumentException: requirement failed
    at scala.Predef$.require(Predef.scala:145)
    at .post(<console>:8)

Vous pouvez rédiger plusieurs exigences ou même ajouter une description à chacune:

def post(tweet: String) = {
  require(tweet.length > 0, "too short message")
  require(tweet.length < 140, "too long message")
  println(tweet)
}

Maintenant, les exceptions sont verbeuses:

scala> post("")
java.lang.IllegalArgumentException: requirement failed: too short message
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:8)

Un autre exemple est ici .


Prime

Vous pouvez effectuer une action chaque fois que l'exigence échoue:

scala> var errorcount = 0
errorcount: Int = 0

def post(tweet: String) = {
  require(tweet.length > 0, {errorcount+=1})
  println(tweet)
  }

scala> errorcount
res14: Int = 0

scala> post("")
java.lang.IllegalArgumentException: requirement failed: ()
    at scala.Predef$.require(Predef.scala:157)
    at .post(<console>:9)
...

scala> errorcount
res16: Int = 1
om-nom-nom
la source
1
requiren'est pas un mot réservé. Ce n'est qu'une méthode définie dans Predef.
missingfaktor
1

Les traits avec des abstract overrideméthodes sont une fonctionnalité de Scala qui n'est pas aussi largement annoncée que beaucoup d'autres. Le but des méthodes avec le abstract overridemodificateur est d'effectuer certaines opérations et de déléguer l'appel à super. Ensuite, ces traits doivent être mélangés avec des implémentations concrètes de leurs abstract overrideméthodes.

trait A {
  def a(s : String) : String
}

trait TimingA extends A {
  abstract override def a(s : String) = {
    val start = System.currentTimeMillis
    val result = super.a(s)
    val dur = System.currentTimeMillis-start
    println("Executed a in %s ms".format(dur))
    result
  }
}

trait ParameterPrintingA extends A {
  abstract override def a(s : String) = {
    println("Called a with s=%s".format(s))
    super.a(s)
  }
}

trait ImplementingA extends A {
  def a(s: String) = s.reverse
}

scala> val a = new ImplementingA with TimingA with ParameterPrintingA

scala> a.a("a lotta as")
Called a with s=a lotta as
Executed a in 0 ms
res4: String = sa attol a

Bien que mon exemple ne soit vraiment pas beaucoup plus qu'un AOP pour homme pauvre, j'ai utilisé ces traits empilables à mon goût pour créer des instances d'interpréteur Scala avec des importations prédéfinies, des liaisons personnalisées et des chemins de classe. Les traits empilables ont permis de créer mon usine le long des lignes de new InterpreterFactory with JsonLibs with LuceneLibspuis d'avoir des importations et des variables de portée utiles pour les scripts des utilisateurs.

MxFr
la source