Ich studiere derzeit Haskell und versuche, ein Projekt zu verstehen, das Haskell zur Implementierung kryptografischer Algorithmen verwendet. Nachdem ich Learn You a Haskell for Great Good online gelesen habe , beginne ich, den Code in diesem Projekt zu verstehen. Dann stellte ich fest, dass ich beim folgenden Code mit dem Symbol "@" festsitze:
-- | Generate an @n@-dimensional secret key over @rq@.
genKey :: forall rq rnd n . (MonadRandom rnd, Random rq, Reflects n Int)
=> rnd (PRFKey n rq)
genKey = fmap Key $ randomMtx 1 $ value @n
Hier ist randomMtx wie folgt definiert:
-- | A random matrix having a given number of rows and columns.
randomMtx :: (MonadRandom rnd, Random a) => Int -> Int -> rnd (Matrix a)
randomMtx r c = M.fromList r c <$> replicateM (r*c) getRandom
Und PRFKey ist unten definiert:
-- | A PRF secret key of dimension @n@ over ring @a@.
newtype PRFKey n a = Key { key :: Matrix a }
Alle Informationsquellen, die ich finden kann, sagen, dass @ das As-Muster ist, aber dieser Code ist anscheinend nicht der Fall. Ich habe das Online-Tutorial, die Blogs und sogar den Sprachbericht für Haskell 2010 unter https://www.haskell.org/definition/haskell2010.pdf überprüft . Auf diese Frage gibt es einfach keine Antwort.
Weitere Codefragmente finden Sie in diesem Projekt auch mit @ auf folgende Weise:
-- | Generate public parameters (\( \mathbf{A}_0 \) and \(
-- \mathbf{A}_1 \)) for @n@-dimensional secret keys over a ring @rq@
-- for gadget indicated by @gad@.
genParams :: forall gad rq rnd n .
(MonadRandom rnd, Random rq, Reflects n Int, Gadget gad rq)
=> rnd (PRFParams n gad rq)
genParams = let len = length $ gadget @gad @rq
n = value @n
in Params <$> (randomMtx n (n*len)) <*> (randomMtx n (n*len))
Ich freue mich sehr über jede Hilfe.
quelle
Antworten:
Das
@n
ist eine erweiterte Funktion des modernen Haskell, die in der Regel nicht durch Übungen wie Lyah abgedeckt, noch kann die den Bericht zu finden.Es ist ein sogenannter Typ - Anwendung und ist eine GHC Spracherweiterung. Um es zu verstehen, betrachten Sie diese einfache polymorphe Funktion
Das intuitive Aufrufen
dup
funktioniert wie folgt:a
x
des zuvor gewählten Typsa
dup
antwortet dann mit einem Wert vom Typ(a,a)
In gewissem Sinne werden
dup
zwei Argumente verwendet: der Typa
und der Wertx :: a
. GHC ist jedoch normalerweise in der Lage, den Typ abzuleitena
(z. B. ausx
oder aus dem Kontext, in dem wir ihn verwendendup
), sodass wir normalerweise nur ein Argument an übergebendup
, nämlichx
. Zum Beispiel haben wirWas ist nun, wenn wir
a
explizit weitergeben wollen? In diesem Fall können wir dieTypeApplications
Erweiterung einschalten und schreibenBeachten Sie die
@...
Argumente mit Typen (keine Werte). Dies ist etwas, das nur zur Kompilierungszeit existiert - zur Laufzeit existiert das Argument nicht.Warum wollen wir das? Nun, manchmal gibt es keine
x
, und wir möchten den Compiler dazu bringen, das richtige auszuwählena
. Z.BTypanwendungen sind häufig in Kombination mit einigen anderen Erweiterungen nützlich, die eine Typinferenz für GHC unmöglich machen, z. B. mehrdeutige Typen oder Typfamilien. Ich werde diese nicht diskutieren, aber Sie können einfach verstehen, dass Sie dem Compiler manchmal wirklich helfen müssen, insbesondere wenn Sie leistungsstarke Funktionen auf Typebene verwenden.
Nun zu Ihrem speziellen Fall. Ich habe nicht alle Details, ich kenne die Bibliothek nicht, aber es ist sehr wahrscheinlich, dass Ihr
n
Wert eine Art natürlicher Zahl auf Typebene darstellt . Hier tauchen wir in ziemlich fortgeschrittenen Erweiterungen, wie den oben genannten plusDataKinds
, und vielleichtGADTs
einigen Maschinen der Typklasse. Obwohl ich nicht alles erklären kann, kann ich hoffentlich einige grundlegende Einblicke geben. Intuitiv,nimmt als Argument
@n
eine Art Kompilierungszeit natürlich, die zur Laufzeit nicht übergeben wird. Stattdessen,dauert
@n
(Kompilierungszeit), zusammen mit einem Beweis , dern
die Bedingung erfülltC n
. Letzteres ist ein Laufzeitargument, das den tatsächlichen Wert von verfügbar machen kannn
. In Ihrem Fall haben Sie in der Tat etwas, das vage ähneltDies ermöglicht es dem Code im Wesentlichen, die natürliche Textebene auf die Begriffebene zu bringen und im Wesentlichen auf den "Typ" als "Wert" zuzugreifen. (Der obige Typ wird übrigens als "mehrdeutig" angesehen - Sie müssen wirklich
@n
klarstellen.)Schließlich: Warum sollte man auf Typebene bestehen wollen,
n
wenn wir das später in die Termstufe umwandeln? Wäre nicht einfacher, einfach Funktionen wie zu schreibenstatt der umständlicheren
Die ehrliche Antwort lautet: Ja, es wäre einfacher. Auf
n
Typebene kann der Compiler jedoch mehr statische Überprüfungen durchführen. Beispielsweise möchten Sie möglicherweise, dass ein Typ "Ganzzahlen modulon
" darstellt und diese hinzufügt. Habenfunktioniert, aber es gibt keine Überprüfung, dass
x
undy
haben den gleichen Modul. Wir könnten Äpfel und Orangen hinzufügen, wenn wir nicht vorsichtig sind. Wir könnten stattdessen schreibenDas ist besser, erlaubt aber trotzdem anzurufen,
foo 5 x y
auch wennn
es nicht so ist5
. Nicht gut. Stattdessen,verhindert, dass etwas schief geht. Der Compiler überprüft statisch alles. Der Code ist schwieriger zu verwenden, ja, aber in gewissem Sinne ist es der springende Punkt, die Verwendung zu erschweren: Wir möchten es dem Benutzer unmöglich machen, etwas mit dem falschen Modul hinzuzufügen.
Fazit: Dies sind sehr fortgeschrittene Erweiterungen. Wenn Sie ein Anfänger sind, müssen Sie sich langsam diesen Techniken nähern. Lassen Sie sich nicht entmutigen, wenn Sie sie nach nur einem kurzen Studium nicht erfassen können. Es dauert einige Zeit. Machen Sie jeweils einen kleinen Schritt und lösen Sie einige Übungen für jede Funktion, um den Sinn zu verstehen. Und du wirst immer StackOverflow haben, wenn du feststeckst :-)
quelle