Ich kann in den API-Dokumenten für Predef sehen, dass es sich um Unterklassen eines generischen Funktionstyps (From) => To handelt, aber das ist alles, was darin steht. Ähm, was? Vielleicht gibt es irgendwo Dokumentation, aber Suchmaschinen verarbeiten "Namen" wie "<: <" nicht sehr gut, so dass ich sie nicht finden konnte.
Folgefrage: Wann sollte ich diese funky Symbole / Klassen verwenden und warum?
typeclass
es die Arbeit dieser Bediener aus? Beispiel :compare :: Ord a => a -> a -> Ordering
? Ich versuche, dieses Scala-Konzept in Bezug auf sein Haskell-Gegenstück zu verstehen.Antworten:
Diese werden als allgemeine Typeinschränkungen bezeichnet . Sie ermöglichen es Ihnen, innerhalb einer typparametrisierten Klasse oder Eigenschaft eine ihrer Typparameter weiter einzuschränken . Hier ist ein Beispiel:
Das implizite Argument
evidence
wird vom Compiler bereitgestellt, iffA
istString
. Sie können als daran denken Beweis , dassA
istString
--die Argument selbst ist nicht wichtig, nur zu wissen , dass es existiert. [edit: Nun, technisch gesehen ist es tatsächlich wichtig, weil es eine implizite Konvertierung vonA
nach darstelltString
, was es dir ermöglicht, aufzurufena.length
und den Compiler dich nicht anschreien zu lassen]Jetzt kann ich es so benutzen:
Aber wenn ich es mit einem
Foo
anderen als einemString
:Sie können diesen Fehler lesen als "konnte keinen Beweis dafür finden, dass Int == String" ... das ist, wie es sein sollte!
getStringLength
legt weitere Beschränkungen für die Art fest,A
als diesFoo
im Allgemeinen erforderlich ist; Sie können nämlich nurgetStringLength
auf a aufrufenFoo[String]
. Diese Einschränkung wird zur Kompilierungszeit erzwungen, was cool ist!<:<
und<%<
arbeiten ähnlich, aber mit geringfügigen Abweichungen:A =:= B
bedeutet, dass A genau B sein mussA <:< B
bedeutet, dass A ein Subtyp von B sein muss (analog zur einfachen Typbeschränkung<:
)A <%< B
bedeutet, dass A als B angezeigt werden muss , möglicherweise durch implizite Konvertierung (analog zur einfachen Typbeschränkung<%
).Dieses Snippet von @retronym ist eine gute Erklärung dafür, wie so etwas früher erreicht wurde und wie allgemeine Typeinschränkungen es jetzt einfacher machen.
NACHTRAG
Um Ihre Folgefrage zu beantworten, ist das Beispiel, das ich gegeben habe, zugegebenermaßen ziemlich erfunden und offensichtlich nicht nützlich. Stellen Sie sich vor, Sie definieren damit eine
List.sumInts
Methode, die eine Liste von Ganzzahlen zusammensetzt. Sie möchten nicht zulassen, dass diese Methode für eine alte Methode aufgerufen wird, sondernList
nur für eineList[Int]
. DerList
Typkonstruktor kann jedoch nicht so eingeschränkt werden. Sie möchten weiterhin Listen mit Zeichenfolgen, Foos, Balken und Dingsbums haben können. Wenn Sie also eine allgemeine Typeinschränkung festlegensumInts
, können Sie sicherstellen, dass nur diese Methode eine zusätzliche Einschränkung aufweist, die nur für a verwendet werden kannList[Int]
. Im Wesentlichen schreiben Sie Sonderfallcode für bestimmte Arten von Listen.quelle
Manifest
, die Sie nicht erwähnt haben.Manifest
sind<:<
und>:>
nur ... da OP genau die 3 Arten von allgemeinen Typbeschränkungen erwähnte, gehe ich davon aus, dass er daran interessiert war.class =:=[From, To] extends From => To
, was bedeutet, dass ein impliziter Wert vom TypFrom =:= To
tatsächlich eine implizite Konvertierung vonFrom
nach istTo
. Wenn Sie also einen impliziten Parameter vom Typ akzeptieren,A =:= String
sagen Sie, dassA
dieser implizit in konvertiert werden kannString
. Wenn Sie die Reihenfolge ändern und das implizite Argument vom TypString =:= A
machen würden, würde es nicht funktionieren, da dies eine implizite Konvertierung vonString
nach wäreA
.From =:= To
im Bereich impliziert, dass Sie eine implizite Konvertierung habenFrom => To
, aber die Implikation läuft nicht rückwärts. Eine implizite KonvertierungA => B
bedeutet nicht, dass Sie eine Instanz von habenA =:= B
.=:=
ist eine versiegelte abstrakte Klasse, die in definiertscala.Predef
ist und nur eine öffentlich exponierte Instanz hat, die implizit und vom Typ istA =:= A
. Sie sind also garantiert, dass ein impliziter Wert des TypsA =:= B
die Tatsache bezeugt, dassA
undB
gleich sind.Keine vollständige Antwort (andere haben dies bereits beantwortet), ich wollte nur Folgendes beachten, was möglicherweise zum besseren Verständnis der Syntax beiträgt: Die Art und Weise, wie Sie diese "Operatoren" normalerweise verwenden, wie zum Beispiel im Beispiel von pelotom:
nutzt die alternative Infix-Syntax von Scala für Typoperatoren .
Also,
A =:= String
ist das gleiche wie=:=[A, String]
(und=:=
ist nur eine Klasse oder ein Merkmal mit einem ausgefallenen Namen). Beachten Sie, dass diese Syntax auch mit "regulären" Klassen funktioniert. Sie können beispielsweise Folgendes schreiben:so was:
Es ähnelt den beiden Syntaxen für Methodenaufrufe, der "normalen" mit
.
und()
und der Operatorsyntax.quelle
makes use of Scala's alternative infix syntax for type operators.
diese Erklärung, ohne die das Ganze keinen Sinn ergibt, völlig fehltLesen Sie die anderen Antworten, um zu verstehen, was diese Konstrukte sind. Hier ist, wann Sie sie verwenden sollten. Sie verwenden sie, wenn Sie eine Methode nur für bestimmte Typen einschränken müssen.
Hier ist ein Beispiel. Angenommen, Sie möchten ein homogenes Paar wie folgt definieren:
Jetzt möchten Sie eine Methode
smaller
wie die folgende hinzufügen :Das funktioniert nur, wenn
T
bestellt wird. Sie können die gesamte Klasse einschränken:Aber das scheint eine Schande zu sein - es könnte Verwendungszwecke für die Klasse geben, wenn sie
T
nicht bestellt wird. Mit einer Typbeschränkung können Sie diesmaller
Methode weiterhin definieren :Es ist in Ordnung zu instanziieren, sagen wir, ein
Pair[File]
, solange Sie nicht nennensmaller
darauf.Im Fall von
Option
wollten die Implementierer eineorNull
Methode, obwohl dies für nicht sinnvoll istOption[Int]
. Durch die Verwendung einer Typeinschränkung ist alles in Ordnung. Sie könnenorNull
ein verwendenOption[String]
, und Sie können ein bildenOption[Int]
und verwenden, solange Sie es nicht aufrufenorNull
. Wenn Sie es versuchenSome(42).orNull
, erhalten Sie die charmante Nachrichtquelle
<:<
und ich denke, dass dasOrdered
Beispiel nicht mehr so überzeugend ist, da Sie jetzt lieber dieOrdering
Typklasse als dasOrdered
Merkmal verwenden würden. So etwas wie :def smaller(implicit ord: Ordering[T]) = if (ord.lt(first, second)) first else second
.Es hängt davon ab, wo sie verwendet werden. Wenn sie beim Deklarieren impliziter Parametertypen verwendet werden, handelt es sich meistens um Klassen. In seltenen Fällen können sie auch Objekte sein. Schließlich können sie Operatoren für
Manifest
Objekte sein. Sie sindscala.Predef
in den ersten beiden Fällen im Inneren definiert , jedoch nicht besonders gut dokumentiert.Sie sollen einen Weg , um die Beziehung zwischen den Klassen zu testen, wie
<:
und<%
zu tun, in Situationen , wenn diese nicht verwendet werden kann.Die Frage "Wann soll ich sie verwenden?" Lautet, sollten Sie dies nicht tun, es sei denn, Sie wissen, dass Sie es sollten. :-) EDIT : Ok, ok, hier sind einige Beispiele aus der Bibliothek. Ein
Either
, Sie haben:Ein
Option
, Sie haben:Weitere Beispiele finden Sie in den Sammlungen.
quelle
:-)
noch einer davon? Und ich würde zustimmen, dass Ihre Antwort auf "Wann sollte ich sie verwenden?" gilt für sehr viele Dinge.