wake-up-neo.net

Versteckte Funktionen von Scala

Was sind die versteckten Funktionen von Scala, die jedem Scala Entwickler bekannt sein sollten?

Ein verstecktes Feature pro Antwort, bitte.

149
Krzysiek Goj

Okay, ich musste noch eins hinzufügen. Jedes Regex Objekt in Scala) hat einen Extraktor (siehe Antwort von oxbox_lakes oben), mit dem Sie auf die Übereinstimmungsgruppen zugreifen können.

// 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"

Die zweite Zeile sieht verwirrend aus, wenn Sie nicht an Pattern Matching und Extraktoren gewöhnt sind. Wann immer Sie ein val oder var definieren, ist das, was nach dem Schlüsselwort kommt, nicht einfach ein Bezeichner, sondern ein Muster. Deshalb funktioniert das:

val (a, b, c) = (1, 3.14159, "Hello, world")

Der rechte Ausdruck erzeugt einen Tuple3[Int, Double, String], Der dem Muster (a, b, c) Entsprechen kann.

In den meisten Fällen verwenden Ihre Muster Extraktoren, die Mitglieder von Singleton-Objekten sind. Zum Beispiel, wenn Sie ein Muster wie schreiben

Some(value)

dann rufen Sie implizit den Extraktor Some.unapply auf.

Sie können aber auch Klasseninstanzen in Mustern verwenden, und genau das geschieht hier. Der Wert regex ist eine Instanz von Regex, und wenn Sie ihn in einem Muster verwenden, rufen Sie implizit regex.unapplySeq (unapply gegenüber unapplySeq auf den Umfang dieser Antwort), die die Übereinstimmungsgruppen in einen Seq[String] extrahiert, dessen Elemente den Variablen Jahr, Monat und Tag zugeordnet sind.

85

Strukturelle Typdefinitionen - d. H. Ein Typ, der durch die von ihm unterstützten Methoden beschrieben wird. Beispielsweise:

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

Beachten Sie, dass Typ des Parameters closeable nur als close-Methode definiert ist

51
oxbow_lakes

Typkonstruktor-Polymorphismus (a.k.a. höherwertige Typen)

Ohne diese Funktion können Sie beispielsweise den Gedanken zum Ausdruck bringen, eine Funktion über eine Liste abzubilden, um eine andere Liste zurückzugeben, oder eine Funktion über einen Baum abzubilden, um einen anderen Baum zurückzugeben. Aber Sie können diese Idee nicht allgemein ohne höhere Arten ausdrücken.

Mit höheren Arten können Sie die Idee von jedem Typ erfassen, der mit einem anderen Typ parametrisiert ist. Ein Typkonstruktor, der einen Parameter annimmt, soll von Art sein (*->*). Zum Beispiel List. Ein Typkonstruktor, der einen anderen Typkonstruktor zurückgibt, ist vom Typ (*->*->*). Zum Beispiel Function1. In Scala haben wir jedoch höhere Arten, sodass wir Typkonstruktoren haben können, die mit anderen Typkonstruktoren parametrisiert sind. Sie sind also von der Art ((*->*)->*).

Beispielsweise:

trait Functor[F[_]] {
  def fmap[A, B](f: A => B, fa: F[A]): F[B]
}

Wenn Sie nun einen Functor[List] Haben, können Sie Listen zuordnen. Wenn Sie einen Functor[Tree] Haben, können Sie über Bäume abbilden. Aber was noch wichtiger ist: Wenn Sie Functor[A] für ein beliebiges A haben (*->*), können Sie eine Funktion über A.

45
Apocalisp

Extractors , mit denen Sie chaotischen if-elseif-else - Code durch Muster ersetzen können. Ich weiß, dass dies nicht genau versteckt ist, aber ich benutze Scala seit ein paar Monaten, ohne die Macht von ihnen wirklich zu verstehen. Seit (einem langen) Beispiel, das ich ersetzen kann:

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

Damit ist meiner Meinung nach viel klarer

implicit val ps: ProductService = ...
val p = code match {
  case SyntheticCodes.Cash(c) => c
  case SyntheticCodes.Forward(f) => f
  case _ => ps.lookupProductByRic(code)
}

Ich muss ein bisschen im Hintergrund arbeiten ...

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

Aber die Beinarbeit lohnt sich, weil sie ein Stück Geschäftslogik in einen vernünftigen Ort trennt. Ich kann meine Product.getCode Methoden wie folgt implementieren.

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

class ForwardProduct {
  def getCode = SyntheticCodes.Forward(this)     
}
39
oxbow_lakes

Manifests Dies ist eine Möglichkeit, die Typinformationen zur Laufzeit abzurufen, als ob Scala=============================.

35
oxbow_lakes

Fallklassen mischen sich automatisch in das Produktmerkmal und bieten untypisierten, indizierten Zugriff auf die Felder, ohne dass eine Reflektion erforderlich ist:

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)

Diese Funktion bietet auch eine vereinfachte Möglichkeit, die Ausgabe der toString -Methode zu ändern:

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

// prints "person: (Aaron,28)" instead of "Person(Aaron, 28)"
println(Person("Aaron", 28)) 
35
Aaron Novstrup

In scala 2.8 können Sie rekursive Methoden verwenden, indem Sie das Paket scala.util.control.TailCalls verwenden (in der Tat ist es Trampolin).

Ein Beispiel:

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)
35
Aymen

Es ist nicht gerade versteckt, aber sicherlich eine unterbeworbene Funktion: scalac -Xprint.

Betrachten Sie zur Veranschaulichung der Verwendung die folgende Quelle:

class A { "xx".r }

Kompilieren Sie dies mit scalac -Xprint: typer Ausgaben:

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

Beachten Sie scala.this.Predef.augmentString("xx").r, bei dem es sich um die Anwendung des in Predef.scala vorhandenen implicit def augmentString Handelt.

scalac -Xprint: <phase> gibt den Syntaxbaum nach einer Compiler-Phase aus. Um die verfügbaren Phasen anzuzeigen, verwenden Sie scalac -Xshow-phase.

Dies ist eine großartige Möglichkeit, um zu erfahren, was sich hinter den Kulissen abspielt.

Versuche es mit

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

verwenden Sie die Typer -Phase, um wirklich zu spüren, wie nützlich es ist.

33
pedrofurla

Sie können Ihre eigenen Kontrollstrukturen definieren. Es sind wirklich nur Funktionen und Objekte und ein bisschen syntaktischer Zucker, aber sie sehen aus und verhalten sich wie die reale Sache.

Der folgende Code definiert beispielsweise dont {...} unless (cond) und 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
  }
}

Jetzt können Sie Folgendes tun:

/* 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) 
30

@switch Anmerkung in Scala 2.8:

Eine Anmerkung, die auf einen Übereinstimmungsausdruck angewendet werden soll. Wenn vorhanden, überprüft der Compiler, ob die Übereinstimmung mit einem Tableswitch oder Lookupswitch kompiliert wurde, und gibt einen Fehler aus, wenn sie stattdessen in eine Reihe bedingter Ausdrücke kompiliert wird.

Beispiel:

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 {
26
missingfaktor

Keine Ahnung, ob das wirklich versteckt ist, aber ich finde es ganz nett.

Typekonstruktoren, die 2 Typparameter annehmen, können in Infixnotation geschrieben werden

object Main {                                                                   
  class FooBar[A, B]

  def main(args: Array[String]): Unit = {
    var x: FooBar[Int, BigInt] = null
    var y: Int FooBar BigInt   = null
  }
}
26
raichoo

in scala 2.8 können Sie @specialized zu Ihren generischen Klassen/Methoden hinzufügen. Dadurch werden spezielle Versionen der Klasse für primitive Typen erstellt (Erweiterung von AnyVal) und die Kosten für unnötiges Ein- und Auspacken gespart : class Foo[@specialized T]...

Sie können eine Teilmenge von AnyVals auswählen: class Foo[@specialized(Int,Boolean) T]...

24
Aymen

In Scala 2.8 wurden Standardargumente und benannte Argumente eingeführt, die das Hinzufügen einer neuen "copy" -Methode ermöglichten, die Scala zu Fallklassen hinzufügt. Wenn Sie dies definieren:

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

und Sie möchten ein neues Foo erstellen, das einem vorhandenen Foo ähnelt, nur mit einem anderen "n" -Wert. Dann können Sie einfach sagen:

foo.copy(n = 3)
24

Die Sprache erweitern. Ich wollte so etwas schon immer in Java (konnte nicht). Aber in Scala Ich kann haben:

  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
  }

und dann schreibe:

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

und bekomme

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

Sie können einen Call-by-Name-Parameter (EDITED: Dies ist ein anderer Parameter als ein Lazy-Parameter!) Für eine Funktion festlegen. Er wird erst ausgewertet, wenn er von der Funktion verwendet wird (EDIT: Tatsächlich wird er jedes Mal neu bewertet, wenn er verwendet wird gebraucht). Siehe diese FAQ für Details

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
*/
23
agilefall

Sie können locally verwenden, um einen lokalen Block einzuführen, ohne Semikolon-Inferenzprobleme zu verursachen.

Verwendung:

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 ist in "Predef.scala" definiert als:

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

Inline bedeutet dies keinen zusätzlichen Aufwand.

20
missingfaktor

Sie können Strukturtypen mit dem Schlüsselwort 'with' erstellen

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
  }
}
17
raichoo

Platzhaltersyntax für anonyme Funktionen

Aus der Scala Sprachspezifikation:

SimpleExpr1 ::= '_'

Ein Ausdruck (der syntaktischen Kategorie Expr) kann an Stellen, an denen Bezeichner zulässig sind, eingebettete Unterstriche _ Enthalten. Ein solcher Ausdruck stellt eine anonyme Funktion dar, bei der nachfolgende Unterstriche aufeinanderfolgende Parameter bezeichnen.

Von Scala Language Changes :

_ + 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)

Damit könnten Sie etwas machen wie:

def filesEnding(query: String) =
  filesMatching(_.endsWith(query))
17
Eugene Yokota

Vorzeitige Initialisierung:

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)

Ausgabe:

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

Wir instanziieren eine anonyme innere Klasse, indem wir das Feld value im Block vor der Klausel with AbstractT2 Initialisieren. Dies garantiert, dass value initialisiert wird, bevor der Text von AbstractT2 Ausgeführt wird, wie beim Ausführen des Skripts gezeigt.

17
missingfaktor

Implizite Definitionen, insbesondere Konvertierungen.

Nehmen Sie zum Beispiel eine Funktion an, die eine Eingabezeichenfolge so formatiert, dass sie zu einer Größe passt, indem Sie die Mitte durch "..." ersetzen:

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
}

Sie können das mit jedem String verwenden und natürlich die toString-Methode verwenden, um alles zu konvertieren. Sie könnten es aber auch so schreiben:

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
}

Und dann könnten Sie Klassen anderer Typen übergeben, indem Sie dies tun:

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

Jetzt können Sie diese Funktion aufrufen, indem Sie ein double übergeben:

sizeBoundedString(12345.12345D, 8)

Das letzte Argument ist implizit und wird aufgrund der impliziten Deklaration automatisch übergeben. Darüber hinaus wird "s" behandelt wie ein String in sizeBoundedString, da eine implizite Konvertierung in String erfolgt.

Implizite dieses Typs sind besser für ungewöhnliche Typen definiert, um unerwartete Konvertierungen zu vermeiden. Sie können eine Konvertierung auch explizit übergeben und sie wird weiterhin implizit in sizeBoundedString verwendet:

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

Sie können auch mehrere implizite Argumente haben, aber dann müssen Sie entweder alle oder keine übergeben. Es gibt auch eine Abkürzungssyntax für implizite Konvertierungen:

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
}

Dies wird genauso verwendet.

Implizite können einen beliebigen Wert haben. Sie können beispielsweise zum Ausblenden von Bibliotheksinformationen verwendet werden. Nehmen Sie zum Beispiel folgendes Beispiel:

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

In diesem Beispiel wird durch Aufrufen von "f" in einem Y-Objekt das Protokoll an den Standarddämon und in einer Instanz von X an den Daemon X-Dämon gesendet. Wenn Sie jedoch g auf einer Instanz von X aufrufen, wird das Protokoll an den explizit angegebenen DefaultDaemon gesendet.

Während dieses einfache Beispiel mit Überladung und privatem Status neu geschrieben werden kann, erfordern Implizite keinen privaten Status und können mit Importen in Zusammenhang gebracht werden.

16

Vielleicht nicht zu versteckt, aber ich denke, das ist nützlich:

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

Dadurch wird automatisch ein Getter und Setter für das Feld generiert, das der Bean-Konvention entspricht.

Weitere Beschreibung unter developerworks

13
agilefall

Implizite Argumente in Abschlüssen.

Ein Funktionsargument kann genau wie bei Methoden als implizit markiert werden. Im Rahmen des Funktionskörpers ist der implizite Parameter sichtbar und kann implizit aufgelöst werden:

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")
  })
}
13
axel22

Erstellen Sie unendliche Datenstrukturen mit Scalas Streams: http://www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patient

12
Aymen

Ergebnistypen sind abhängig von der impliziten Auflösung. Dies kann Ihnen eine Form des Mehrfachversands geben:

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] = [email protected]

scala> implicit val intToDouble = new PerformFunc[Int,Double] {
  def perform(a : Int) = 1.0
}
intToDouble: Java.lang.Object with PerformFunc[Int,Double] = [email protected]

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
12
jsuereth

Skalas Äquivalent von Java double brace initializer.

Mit Scala können Sie eine anonyme Unterklasse mit dem Hauptteil der Klasse (dem Konstruktor) erstellen, die Anweisungen zum Initialisieren der Instanz dieser Klasse enthält.

Dieses Muster ist sehr nützlich, wenn komponentenbasierte Benutzeroberflächen (z. B. Swing, Vaadin) erstellt werden, da Benutzeroberflächenkomponenten erstellt und ihre Eigenschaften präziser deklariert werden können.

Siehe http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-Java-development-reid-2011.pdf für weitere Informationen.

Hier ist ein Beispiel für das Erstellen einer Vaadin-Schaltfläche:

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

Ausschließen von Mitgliedern aus import -Anweisungen

Angenommen, Sie möchten ein Logger verwenden, das eine println - und eine printerr -Methode enthält. Sie möchten jedoch nur die Methode für Fehlermeldungen verwenden und die gute alte Predef.println für Standardausgabe. Sie könnten dies tun:

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

wenn logger jedoch noch zwölf weitere Methoden enthält, die Sie importieren und verwenden möchten, ist es unpraktisch, sie aufzulisten. Sie könnten stattdessen versuchen:

import logger.{println => donotuseprintlnt, _}

dies "verschmutzt" jedoch immer noch die Liste der importierten Mitglieder. Geben Sie den übermächtigen Platzhalter ein:

import logger.{println => _, _}

und das reicht genau das Richtige ™.

3
Philippe

require Methode (definiert in Predef), mit der Sie zusätzliche Funktionseinschränkungen definieren können, die zur Laufzeit überprüft werden. Stellen Sie sich vor, Sie entwickeln einen weiteren Twitter-Client und müssen die Tweet-Länge auf 140 Symbole begrenzen. Außerdem kannst du keinen leeren Tweet posten.

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

Wenn Sie nun post mit einem Argument unangemessener Länge aufrufen, wird eine Ausnahme ausgelöst:

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)

Sie können zu jeder Anforderung mehrere Anforderungen schreiben oder sogar eine Beschreibung hinzufügen:

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

Jetzt sind Ausnahmen ausführlich:

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

Ein weiteres Beispiel ist hier .


Bonus

Sie können eine Aktion jedes Mal ausführen, wenn eine Anforderung fehlschlägt:

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
2
om-nom-nom

Merkmale mit abstract override - Methoden sind eine Funktion in Scala, für die so wenig Werbung gemacht wird wie für viele andere. Methoden mit dem Modifikator abstract override Sollen einige Operationen ausführen und den Aufruf an super delegieren. Dann müssen diese Eigenschaften mit konkreten Implementierungen ihrer abstract override - Methoden gemischt werden.

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

Mein Beispiel ist zwar nicht viel mehr als ein AOP eines armen Mannes, aber ich habe diese stapelbaren Eigenschaften sehr nach meinem Geschmack verwendet, um Scala zu bauen. Interpreterinstanzen mit vordefinierten Importen, benutzerdefinierten Bindungen und Klassenpfaden. Die Stackable Traits ermöglichten es, meine Factory nach new InterpreterFactory with JsonLibs with LuceneLibs Zu erstellen und dann nützliche Import- und Gültigkeitsbereichsvariablen für die Benutzerskripte zu haben .

1
MxFr