Gibt es in Haskell eine Möglichkeit, auszudrücken, dass ein Typ auf mehrere Arten eine Instanz einer Typklasse sein sollte?

7

(Sorry im Voraus, wenn die Frage dumm oder offensichtlich ist - ich habe nicht viel Erfahrung mit Haskell).

Gibt es eine Möglichkeit auszudrücken, dass ein Typ auf mehrere Arten eine Instanz einer Typklasse sein sollte? Dies wird am besten anhand eines Beispiels illustriert (was wahrscheinlich etwas doof ist): In der Mathematik können wir sagen, dass ein Semiring eine Menge ist, die ein kommutatives Monoid unter einer Operation ist (die wir Addition, Identität 0 nennen) und ein Monoid unter eine andere (die wir Multiplikation nennen) zusammen mit den Anforderungen, die Multiplikation über Addition verteilt und dass 0 alle Elemente unter Multiplikation vernichtet. Die letzteren Teile sind hier nicht wichtig.

Angenommen, ich habe eine Typklasse Monoid (nicht zu verwechseln mit Data.Monoid ),

%Vor%

und möchte eine Typklasse Semiring erstellen. Aus der obigen Definition möchte ich sagen: "Wenn der Typ r in zwei ( distinct ) Weisen ein Monoid ist, nennen wir ihn Semiring". Also möchte ich etwas wie

%Vor%

was natürlich nicht funktioniert. Zugegeben, das Beispiel wird ein bisschen merkwürdig, da es keine Funktionen mehr gibt, die wir für Semirings benötigen würden, also wäre die Typklasse leer, aber ich hoffe, dass es illustriert, worum ich frage (oder einfach so tun, als ob wir eine Funktion benötigen f:r->r für Semiring r ).

Also, in der allgemeinen Einstellung frage ich: Gegeben eine Typklasse A , gibt es eine Möglichkeit, eine Typklasse B a mit der Anforderung zu parametrisieren, dass a eine Instanz von A auf zwei Arten sein kann (Das heißt, a sollte die Funktionen, die von A angegeben wurden, auf zwei Arten implementieren)?

    
gspr 22.10.2010, 16:08
quelle

5 Antworten

11

Eine Möglichkeit besteht darin, für die beiden Operationen eines Semirings eigene Monoide zu definieren:

%Vor%

und kombinieren Sie sie dann:

%Vor%

Das Problem ist, dass Sie die Monoidgesetze oder die Tatsache, dass eine Operation kommutativ ist, nicht ausdrücken können. Das Beste, was Sie bekommen können, ist das Definieren von Quickcheck-Eigenschaften für die Gesetze.

Für etwas Inspiration sehen Sie sich das numerische Vorspiel und dieses Papier.

    
Daniel Velkov 22.10.2010, 17:41
quelle
7

Für Monoid wird dies speziell mit Typ-Wrappern gemacht. Wenn Sie in das Modul Data.Monoid schauen, finden Sie zwei verschiedene monoidale Strukturen für Bool -Werte: Any und All , sowie zwei verschiedene Strukturen für Typen, die Num : Sum und implementieren Product und zwei Strukturen für Maybe types: First und Last .

Sie werden jedoch Probleme mit Ihrem Semiring-Beispiel bekommen, da die monoidalen Strukturen für Sum und Product beide Implementierungen von mempty (Haskell-Version von unit ) und mappend (Haskell Version von operation ).

    
quelle
4

Andere Antworten haben newtype wrappers erwähnt, aber keine explizite Lösung mit ihnen gegeben:

%Vor%

Sie benötigen einige GHC-Erweiterungen wie FlexibleContexts .

    
Reid Barton 23.10.2010 06:00
quelle
3

Siehe auch Conor McBride's "rhythm section" -Post: Ссылка , obwohl das so ist die Wertebene und hilft nicht mit Typklassen.

Kmetts Monoids-Bibliothek (bevor er den Ring-Kram ausgezogen hat) hat etwas Ähnliches wie Daniel Velkovs Ansatz umgesetzt: Ссылка

Ich füge hinzu, dass das Schöne an diesem Ansatz ist, dass Sie durch die explizite Definition von additiv und multiplikativ auf einen Datentyp erfassen können, dass sie nicht die gleichen sind - dh, die letztere verteilt sich auf die erste.

>     
sclv 22.10.2010 17:49
quelle
2

Die übliche Technik dafür ist, wie andere Antworten erwähnen, Wrapper vom Newtyp. In vielen Fällen scheint mir das ein Missbrauch des Typklassenbegriffs zu sein. Typklassen sind logische "Axiome", die angeben, dass ein Typ für einen Typ wahr ist; zum Beispiel, dass Maybe eine Monade ist, oder dass Int eine Num ist, oder dass Listen geordnet sind, wenn ihre Elemente sind. Wie im Fall von Eq und Ord gibt es oft andere vernünftige Definitionen, aber die gewählten sind irgendwie "kanonisch". Andere Zeiten, wie im Fall von Monoid, gibt es nicht.

Im Fall von Monoid und anderen hoch abstrakten Strukturen wie dieser bin ich der Meinung, dass eine data -Deklaration nützlicher wäre. Zum Beispiel data Monoid a = Monoid {mempty :: a ; mappend :: a -> a -> a} . Dann haben wir addition :: Num a => Monoid a , liftMaybe :: Monoid a -> Monoid (Maybe a) , etc.

Dieselbe Technik könnte verwendet werden, um Ihr Semiring zu implementieren. Zum Beispiel (mit einem Monoid-Datentyp wie zuvor): data Semiring a = Semiring { addition, multiplication :: Monoid a } .

    
mokus 22.10.2010 20:01
quelle

Tags und Links