Was ist Levity-Polymorphismus?

79

Wie der Titel der Frage zeigt, möchte ich wissen, was Levity-Polymorphismus ist und was seine Motivation ist. Ich weiß, dass diese Seite einige Details enthält, aber die meisten Erklärungen gehen mir über den Kopf. :) :)

Obwohl diese Seite etwas freundlicher ist, kann ich die Motivation dahinter immer noch nicht verstehen.

Sibi
quelle

Antworten:

79

Hinweis: Diese Antwort basiert auf jüngsten Beobachtungen zu Levity-Diskussionen. Alles, was den Levity-Polymorphismus betrifft, ist derzeit nur in den GHC 8.0-Release-Kandidaten implementiert und kann sich daher ändern (siehe z. B. # 11471 ).


TL; DR : Es ist eine Möglichkeit, Funktionen gegenüber angehobenen und nicht angehobenen Typen polymorph zu machen, was mit regulären Funktionen nicht möglich ist. Zum Beispiel gibt der folgende Code keine Typprüfung mit regulären Polymorphismen ein, da Int#hat Art #, aber die Typvariablen in idhaben Art *:

{-# LANGUAGE MagicHash #-}

import GHC.Prim

example :: Int# -> Int# 
example = id            -- does not work, since id :: a -> a
Couldn't match kind ‘*’ with ‘#’
When matching types
  a0 :: *
  Int# :: #
Expected type: Int# -> Int#
  Actual type: a0 -> a0
In the expression: id

Beachten Sie, dass (->)immer noch etwas Magie verwendet wird.


Bevor ich diese Frage beantworte, gehen wir einen Schritt zurück und gehen zu einer der am häufigsten verwendeten Funktionen ($).

Was ist ($)der Typ? Nun, laut Hackage und dem Bericht ist es

($) :: (a -> b) -> a -> b

Das ist jedoch nicht 100% vollständig. Es ist eine bequeme kleine Lüge. Das Problem ist, dass polymorphe Typen (wie aund b) Art haben *. (Bibliotheks-) Entwickler wollten jedoch ($)nicht nur Typen mit Art verwenden *, sondern auch solche #, z

unwrapInt :: Int -> Int#

Während Inthat Art *(es kann unten sein), Int#hat Art #(und kann überhaupt nicht unten sein). Der folgende Code wird dennoch überprüft:

unwrapInt $ 42

Das sollte nicht funktionieren. Erinnern Sie sich an den Rückgabetyp von ($)? Es war polymorph, und polymorphe Typen haben Art *, nicht #! Warum hat es funktioniert? Zuerst war es ein Fehler , und dann war es ein Hack (Auszug aus einer Mail von Ryan Scott auf der Mailingliste von ghc-dev):

Warum passiert das?

Die lange Antwort ist , dass vor der GHC 8.0, in der Art Unterschrift ($) :: (a -> b) -> a -> b, bwar eigentlich nicht in der Art *, sondern OpenKind. OpenKindist ein schrecklicher Hack, der es sowohl angehobenen (Art *) als auch nicht angehobenen (Art #) Typen erlaubt, ihn zu bewohnen, weshalb (unwrapInt $ 42) Typechecks.

Was ist ($)der neue Typ in GHC 8.0? Es ist

($) :: forall (w :: Levity) a (b :: TYPE w). (a -> b) -> a -> b
-- will likely change according to Richard E.

Um es zu verstehen, müssen wir uns Folgendes ansehen Levity:

data Levity = Lifted | Unlifted

Jetzt können wir uns ($)einen der folgenden Typen vorstellen, da es nur zwei Möglichkeiten gibt w:

-- pseudo types
($) :: forall a (b :: TYPE   Lifted). (a -> b) -> a -> b
($) :: forall a (b :: TYPE Unlifted). (a -> b) -> a -> b

TYPEist eine magische Konstante und definiert die Arten *und #als neu

type * = TYPE Lifted
type # = TYPE Unlifted

Die Quantifizierung über Arten ist ebenfalls ziemlich neu und Teil der Integration abhängiger Typen in Haskell .

Der Name Levity-Polymorphismus kommt von der Tatsache, dass Sie jetzt polymorphe Funktionen sowohl über angehobene als auch über nicht angehobene Typen schreiben können, was mit den vorherigen Einschränkungen des Polymorphismus nicht erlaubt / möglich war. OpenKindGleichzeitig wird auch der Hack beseitigt. Es geht wirklich "nur" darum, mit beiden Arten von Arten umzugehen.

Übrigens sind Sie mit Ihrer Frage nicht allein. Sogar Simon Peyton Jones sagte, dass eine Levity-Wiki-Seite erforderlich sei , und Richard E. (der aktuelle Implementierer) erklärte, dass die Wiki-Seite ein Update des aktuellen Prozesses benötigt.

Verweise

Zeta
quelle
3
"Umgang mit beiden Arten von Arten" :)
Sibi
4
Huh. Ich denke, ich würde einen hässlichen Hack bevorzugen, der dieses Zeug versteckt hält.
Jberryman
3
Warum ist es nicht ($) :: forall (w1 w2 :: Levity) (a :: TYPE w1) (b :: TYPE w2). (a -> b) -> a -> b, dh warum ($)soll es nicht mit Funktionen mit nicht aufgehobenen Argumenten verwendbar sein?
Kaktus
3
@Cactus, der im "Bug" besprochen wird, siehe diesen Kommentar von SPJ: ghc.haskell.org/trac/ghc/ticket/8739#comment:6
Zeta
1
@jberryman: # 11549 versteckt das RuntimeRep(ersetzt Levity).
Zeta