Ok, werfen wir einen Blick darauf:
%Vor% Die Klasse ist ein AnyVal
wrapper; Während ich die implicit
Umwandlung von einem schnellen Blick nicht wirklich sehen kann, riecht es wie das "pimp my library" -Muster. Ich vermute also, dass <>
als "Erweiterungsmethode" für einige (oder vielleicht alle) Typen hinzugefügt werden soll.
@inline
ist eine Annotation, eine Möglichkeit, Metadaten auf etwas zu setzen. Dies ist ein Hinweis für den Compiler, dass dies inline sein sollte. <>
ist der Methodenname - viele Dinge, die wie "Operatoren" aussehen, sind einfache Methoden in scala.
Die von Ihnen verlinkte Dokumentation hat die R: ClassTag
bereits auf gewöhnliche R
und eine implicit ClassTag[R]
erweitert - das ist ein "context bound" und es ist einfach syntaktischer Zucker. ClassTag
ist ein vom Compiler generiertes Ding, das für jeden (konkreten) Typ existiert und bei der Reflektion hilft, also ist dies ein Hinweis darauf, dass die Methode wahrscheinlich irgendwann an einem R
reflektiert.
Nun, das Fleisch: Dies ist eine generische Methode, die durch zwei Typen parametrisiert wird: [R, U]
. Seine Argumente sind zwei Funktionen, f: U => R
und g: R => Option[U]
. Das sieht ein wenig wie das funktionale Konzept Prism
aus - eine Konvertierung von U
nach R
, die immer funktioniert, und eine Konvertierung von R
nach U
, die manchmal nicht funktioniert.
Der interessante Teil der Signatur (Art) ist der implicit shape
am Ende. Shape
wird als "typeclass" bezeichnet, daher ist dies wohl am besten als "constraint" gedacht: es begrenzt die möglichen Typen U
und R
, mit denen wir diese Funktion bezeichnen können, auf diejenigen, für die ein Das entsprechende Shape
ist verfügbar.
Sehen Sie sich die Dokumentation für Shape
, sehen wir, dass die vier Typen Level
, Mixed
, Unpacked
und Packed
sind. Die Einschränkung ist also: Es muss ein Shape
sein, dessen "Level" ein Untertyp von FlatShapeLevel
ist, wobei der Mixed
Typ T
ist und der Unpacked
Typ R
(der Packed
Typ kann ein beliebiger Typ sein.)
Also ist dies eine Typ-Level-Funktion, die ausdrückt, dass R
"die entpackte Version von" T
ist. Um das Beispiel aus der Shape
-Dokumentation wieder zu verwenden, wenn T
ist (Column[Int], Column[(Int, String)], (Int, Option[Double]))
, dann R
wird (Int, (Int, String), (Int, Option[Double])
(und es funktioniert nur für FlatShapeLevel
, aber ich werde eine Beurteilung machen, dass das ist wahrscheinlich nicht wichtig). U
ist interessanterweise völlig unbeschränkt.
Damit können wir ein MappedProjection[unpacked-version-of-T, U]
von jedem T
erzeugen, indem wir Konvertierungsfunktionen in beide Richtungen bereitstellen. In einer einfachen Version ist also T
eine Column[String]
- eine Repräsentation einer Spalte String
in einer Datenbank - und wir möchten sie als einen anwendungsspezifischen Typ darstellen, z. %Code%. Also EmailAddress
, R=String
, und wir bieten Konvertierungsfunktionen in beide Richtungen: U=EmailAddress
und f: EmailAddress => String
. Es macht Sinn, dass es so ist: Jedes g: String => Option[EmailAddress]
kann als EmailAddress
dargestellt werden (zumindest sollten sie besser sein, wenn wir sie in der Datenbank speichern wollen), aber nicht jedes String
ist eine gültige String
. Wenn unsere Datenbank irgendwie z. " Ссылка " in der E-Mail-Adressspalte würde% code%% EmailAddress
zurückgeben, und Slick könnte das elegant handhaben.
g
selbst ist leider undokumentiert. Aber ich vermute, es ist eine Art von fauler Darstellung einer Sache, die wir abfragen können; wo wir ein None
hatten, haben wir jetzt ein Pseudo-Spalten-Ding, dessen (unterliegender) Typ MappedProjection
ist. Dies erlaubt uns, Pseudo-Anfragen wie 'Wählen Sie von Benutzern, bei denen emailAddress.domain =' gmail.com '' zu schreiben, was direkt in der Datenbank nicht möglich wäre (die nicht wissen, welcher Teil einer Email-Adresse ist) die Domäne), ist aber einfach mit Hilfe von Code zu tun. Zumindest ist das meine beste Schätzung, was es tun könnte.
Möglicherweise könnte die Funktion klarer gemacht werden, indem ein Standardtyp Column[String]
(z. B. der von Monocle) verwendet wird, statt ein Paar Funktionen explizit zu übergeben. Die Verwendung des Impliziten zur Bereitstellung einer Typ-Level-Funktion ist umständlich, aber notwendig. In einer vollständig abhängigen typisierten Sprache (z. B. Idris) könnten wir unsere Typ-Level-Funktion als eine Funktion schreiben (etwas wie EmailAddress
). Diese Funktion sieht konzeptionell so aus:
Dies erklärt hoffentlich einige Gedanken zum Lesen einer neuen, unbekannten Funktion. Ich kenne Slick überhaupt nicht, also habe ich keine Ahnung, wie genau ich bin, wofür dieses Prism
verwendet wird - habe ich es richtig verstanden?