Was sind die versteckten Funktionen von Scala, die jeder Scala-Entwickler kennen sollte?
Bitte eine versteckte Funktion pro Antwort.
scala
hidden-features
Krzysiek Goj
quelle
quelle
Antworten:
Okay, ich musste noch einen hinzufügen. Jedes
Regex
Objekt in Scala verfügt über einen Extraktor (siehe Antwort von oxbox_lakes oben), mit dem Sie auf die Übereinstimmungsgruppen zugreifen können. Sie können also Folgendes tun:Die zweite Zeile sieht verwirrend aus, wenn Sie nicht daran gewöhnt sind, Mustervergleich und Extraktoren zu verwenden. Wann immer Sie ein
val
oder definierenvar
, ist das, was nach dem Schlüsselwort kommt, nicht einfach eine Kennung, sondern ein Muster. Deshalb funktioniert das:Der Ausdruck für
Tuple3[Int, Double, String]
die rechte Hand erzeugt ein, das dem Muster entsprechen kann(a, b, c)
.In den meisten Fällen verwenden Ihre Muster Extraktoren, die Mitglieder von Singleton-Objekten sind. Zum Beispiel, wenn Sie ein Muster wie schreiben
dann rufen Sie implizit den Extraktor auf
Some.unapply
.Sie können aber auch Klasseninstanzen in Mustern verwenden, und genau das passiert hier. Der val regex ist eine Instanz von
Regex
, und wenn Sie ihn in einem Muster verwenden, rufen Sie implizit aufregex.unapplySeq
(unapply
versusunapplySeq
geht über den Rahmen dieser Antwort hinaus), wodurch die Übereinstimmungsgruppen in a extrahiert werdenSeq[String]
, deren Elemente zugewiesen werden, um die Variablen Jahr, Monat und Tag.quelle
Strukturelle Typdefinitionen - dh ein Typ, der durch die unterstützten Methoden beschrieben wird. Beispielsweise:
Beachten Sie, dass der Typ des Parameters
closeable
nur mit einerclose
Methode definiert istquelle
Typ-Konstruktor-Polymorphismus (auch bekannt als höherwertige Typen)
Ohne diese Funktion können Sie beispielsweise die Idee ausdrücken, eine Funktion einer Liste zuzuordnen, um eine andere Liste zurückzugeben, oder eine Funktion einem Baum zuzuordnen, um einen anderen Baum zurückzugeben. Aber ohne höhere Arten kann man diese Idee nicht generell ausdrücken .
Mit höheren Arten können Sie die Idee eines beliebigen Typs erfassen , der mit einem anderen Typ parametrisiert ist. Ein Typkonstruktor, der einen Parameter akzeptiert, wird als Art bezeichnet
(*->*)
. Zum BeispielList
. Ein Typkonstruktor, der einen anderen Typkonstruktor zurückgibt, wird als Art bezeichnet(*->*->*)
. Zum BeispielFunction1
. In Scala gibt es jedoch höhere Arten, sodass wir Typkonstruktoren haben können, die mit anderen Typkonstruktoren parametrisiert werden. Also sind sie von der Art wie((*->*)->*)
.Beispielsweise:
Wenn Sie eine haben
Functor[List]
, können Sie jetzt Listen zuordnen. Wenn Sie eine habenFunctor[Tree]
, können Sie über Bäume kartieren. Aber was noch wichtiger ist, wenn SieFunctor[A]
für eine Art von A haben(*->*)
, können Sie eine Funktion überordnenA
.quelle
Extraktoren, mit denen Sie unordentlichen
if-elseif-else
Code durch Muster ersetzen können. Ich weiß, dass diese nicht genau versteckt sind, aber ich benutze Scala seit einigen Monaten, ohne die Macht von ihnen wirklich zu verstehen. Für (ein langes) Beispiel kann ich ersetzen:Damit ist das meiner Meinung nach viel klarer
Ich muss im Hintergrund ein bisschen Beinarbeit machen ...
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 .quelle
Manifeste, mit denen die Typinformationen zur Laufzeit abgerufen werden können, als hätte Scala Typen geändert.
quelle
In Scala 2.8 können Sie mithilfe des Pakets scala.util.control.TailCalls (in der Tat Trampolin) Methoden mit rekursiver Schwanzbildung verwenden.
Ein Beispiel:
quelle
Fallklassen mischen automatisch das Produktmerkmal und bieten einen untypisierten, indizierten Zugriff auf die Felder ohne Reflexion:
Diese Funktion bietet auch eine vereinfachte Möglichkeit, die Ausgabe der
toString
Methode zu ändern :quelle
Es ist nicht gerade versteckt, aber sicherlich eine unterbeworbene Funktion: scalac -Xprint .
Betrachten Sie zur Veranschaulichung der Verwendung die folgende Quelle:
Kompilieren Sie dies mit scalac -Xprint: typer- Ausgaben:
Beachten Sie
scala.this.Predef.augmentString("xx").r
, dass dies eine Anwendung derimplicit def augmentString
Gegenwart in Predef.scala ist.scalac -Xprint: <phase> druckt den Syntaxbaum nach einer Compilerphase. Um die verfügbaren Phasen anzuzeigen, verwenden Sie scalac -Xshow-Phasen .
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 sie ist.
quelle
Sie können Ihre eigenen Kontrollstrukturen definieren. Es sind wirklich nur Funktionen und Objekte und etwas syntaktischer Zucker, aber sie sehen aus und verhalten sich wie echt.
Der folgende Code definiert beispielsweise
dont {...} unless (cond)
unddont {...} until (cond)
:Jetzt können Sie Folgendes tun:
quelle
zif[A : Zero](cond: => Boolean)(t: => A): A = if(cond) t else mzero
. Benötigt Scalaz.@switch
Anmerkung in Scala 2.8:Beispiel:
quelle
Keine Ahnung, ob das wirklich versteckt ist, aber ich finde es ganz nett.
Typkonstruktoren, die zwei Typparameter verwenden, können in Infixnotation geschrieben werden
quelle
var foo2barConverter: Foo ConvertTo Bar
würde die Reihenfolge der Typparameter offensichtlich sein.Scala 2.8 führte Standard- und benannte Argumente ein, die das Hinzufügen einer neuen "Kopier" -Methode ermöglichten, die Scala zu Fallklassen hinzufügt. Wenn Sie dies definieren:
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:
quelle
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 Boxen / Unboxing gespart:
class Foo[@specialized T]...
Sie können eine Teilmenge von AnyVals auswählen:
class Foo[@specialized(Int,Boolean) T]...
quelle
Sprache erweitern. Ich wollte schon immer so etwas in Java machen (konnte nicht). Aber in Scala kann ich haben:
und dann schreibe:
und bekomme
quelle
Sie können einen Call-by-Name-Parameter (EDITED: Dies unterscheidet sich von einem Lazy-Parameter!) Zu einer Funktion festlegen. Er wird erst ausgewertet, wenn er von der Funktion verwendet wird (EDIT: Tatsächlich wird er jedes Mal neu bewertet gebraucht). Einzelheiten finden Sie in dieser FAQ
quelle
lazy val xx: Bar = x
in Ihrer Methode tun und von diesem Moment an nur noch verwendenxx
.Sie können
locally
einen lokalen Block einführen, ohne Semikolon-Inferenzprobleme zu verursachen.Verwendung:
locally
wird in "Predef.scala" definiert als:Inline zu sein, bedeutet keinen zusätzlichen Overhead.
quelle
Frühe Initialisierung:
Ausgabe:
quelle
Sie können Strukturtypen mit dem Schlüsselwort 'with' erstellen
quelle
Platzhaltersyntax für anonyme Funktionen
Aus der Scala-Sprachspezifikation:
Von Scala Sprachänderungen :
Mit diesem könnten Sie etwas tun wie:
quelle
Implizite Definitionen, insbesondere Konvertierungen.
Nehmen Sie beispielsweise eine Funktion an, die eine Eingabezeichenfolge so formatiert, dass sie zu einer Größe passt, indem Sie die Mitte durch "..." ersetzen:
Sie können dies mit jedem String verwenden und natürlich die toString-Methode verwenden, um alles zu konvertieren. Man könnte es aber auch so schreiben:
Und dann könnten Sie Klassen anderer Typen übergeben, indem Sie dies tun:
Jetzt können Sie diese Funktion aufrufen, indem Sie ein Double übergeben:
Das letzte Argument ist implizit und wird aufgrund der impliziten Deklaration automatisch übergeben. Darüber hinaus „s“ wird behandelt wie eine Zeichenfolge innerhalb sizeBoundedString weil es eine implizite Konvertierung von ihm Zeichenfolge ist.
Implizite dieses Typs sind für ungewöhnliche Typen besser definiert, um unerwartete Konvertierungen zu vermeiden. Sie können eine Konvertierung auch explizit übergeben, und sie wird weiterhin implizit in sizeBoundedString verwendet:
Sie können auch mehrere implizite Argumente haben, aber dann müssen Sie entweder alle übergeben oder keines von ihnen übergeben. Es gibt auch eine Verknüpfungssyntax für implizite Konvertierungen:
Dies wird genauso verwendet.
Implizite können jeden Wert haben. Sie können beispielsweise verwendet werden, um Bibliotheksinformationen auszublenden. Nehmen Sie zum Beispiel das folgende Beispiel:
In diesem Beispiel wird beim Aufrufen von "f" in einem Y-Objekt das Protokoll an den Standarddämon und auf einer Instanz von X an den Daemon X-Dämon gesendet. Wenn Sie jedoch g für eine 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 einen Kontext gebracht werden.
quelle
Vielleicht nicht zu versteckt, aber ich denke, das ist nützlich:
Dadurch werden automatisch ein Getter und ein Setter für das Feld generiert, die der Bean-Konvention entsprechen.
Weitere Beschreibung bei developerworks
quelle
Implizite Argumente in Abschlüssen.
Ein Funktionsargument kann genauso wie bei Methoden als implizit markiert werden. Im Rahmen des Funktionskörpers ist der implizite Parameter sichtbar und kann implizit aufgelöst werden:
quelle
Erstellen Sie unendliche Datenstrukturen mit Scala
Stream
: http://www.codecommit.com/blog/scala/infinite-lists-for-the-finitely-patientquelle
Ergebnistypen hängen von der impliziten Auflösung ab. Dies kann Ihnen eine Form des Mehrfachversands geben:
quelle
foo
Verwendungen,a
die vor der Ausführung dieser Befehle in der Umgebung vorhanden sein müssen. Ich nehme an, du meintestz.perform(x)
.Scalas Äquivalent zum Java Double Brace Initialisierer.
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 beim Erstellen komponentenbasierter Benutzeroberflächen (z. B. Swing, Vaadin), da es das Erstellen von UI-Komponenten und das präzisere Deklarieren ihrer Eigenschaften ermöglicht.
Weitere Informationen finden Sie unter http://spot.colorado.edu/~reids/papers/how-scala-experience-improved-our-java-development-reid-2011.pdf .
Hier ist ein Beispiel für das Erstellen einer Vaadin-Schaltfläche:
quelle
Mitglieder von
import
Erklärungen ausschließenAngenommen, Sie möchten eine Methode verwenden
Logger
, die eineprintln
und eineprinterr
Methode enthält, aber nur die für Fehlermeldungen verwenden und die gute altePredef.println
für die Standardausgabe beibehalten. Sie könnten dies tun:Wenn Sie jedoch
logger
weitere zwölf Methoden enthalten, die Sie importieren und verwenden möchten, ist es unpraktisch, diese aufzulisten. Sie könnten stattdessen versuchen:Dies "verschmutzt" jedoch immer noch die Liste der importierten Mitglieder. Geben Sie den übermächtigen Platzhalter ein:
und das wird genau das Richtige tun ™.
quelle
require
Methode (definiert inPredef
), 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 beschränken. Außerdem kannst du keinen leeren Tweet posten.Wenn Sie jetzt einen Beitrag mit einem Argument mit unangemessener Länge aufrufen, wird eine Ausnahme ausgelöst:
Sie können mehrere Anforderungen schreiben oder sogar eine Beschreibung hinzufügen:
Jetzt sind Ausnahmen ausführlich:
Ein weiteres Beispiel ist hier .
Bonus
Sie können jedes Mal eine Aktion ausführen, wenn die Anforderung fehlschlägt:
quelle
require
ist kein reserviertes Wort. Es ist nur eine Methode definiert inPredef
.Merkmale mit
abstract override
Methoden sind ein Merkmal in Scala, das nicht so häufig beworben wird wie viele andere. Mit Methoden mit demabstract override
Modifikator sollen einige Operationen ausgeführt und der Aufruf an delegiert werdensuper
. Dann müssen diese Merkmale mit konkreten Implementierungen ihrerabstract override
Methoden gemischt werden.Während mein Beispiel wirklich nicht viel mehr ist als das AOP eines armen Mannes, habe ich diese stapelbaren Eigenschaften sehr nach meinem Geschmack verwendet, um Scala-Interpreter-Instanzen mit vordefinierten Importen, benutzerdefinierten Bindungen und Klassenpfaden zu erstellen. Die stapelbaren Eigenschaften ermöglichten es, meine Factory nach dem Vorbild von zu erstellen
new InterpreterFactory with JsonLibs with LuceneLibs
und dann nützliche Importe und Bereichsvariablen für die Benutzerskripte zu haben.quelle