2017-01-16 3 views
1

Ist es möglich, eine GHC-Erweiterung zu verwenden, um eine neue Typklasse zu definieren, die zu Tupeln beliebiger Länge verallgemeinert wird?Neue Typklasse mit generischer Instanzdefinition für Tupel beliebiger Länge

Es gab bereits einige Fragen über das Verhalten von eingebauten Klassen in Prelude und Base (einige Klassen unterstützen bis zu 15-Elemente Tupel, einige bis zu 7) und über die (Nicht-) Möglichkeit, diese Klassen zu erweitern.

Präludium und Basisverhalten: Haskell Tuple Size Limit

erstreckt Show mit neuen Definitionen: Extend a Show instance to a Tuple of any size

ich eine etwas andere Frage zu stellen. Wenn ich eine völlig neue Typklasse erstelle, ist es möglich, eine Instanzregel hinzuzufügen, die Tupel beliebiger Länge behandelt (möglicherweise unter Verwendung einer GHC-Erweiterung)?

Hier ist ein Beispiel für eine solche Klasse namens PartialOrder. Ich mag Tupel beliebiger Größe ermöglichen, teilweise mit einer Definition

(a,b ... , z) <= (a1,b1, ... , z1) iff (a <= a1) && (b <= b1) && ... && (z <= z1) 

Hier ist mein erster Stich mit der folgenden Regel im Vergleich zu dem traditionellen „definieren die Klasse für Tupel bis zu einer beliebigen Größe“ Ansatz.

Gibt es eine GHC-Erweiterung, die zum Schreiben von Instanzdefinitionen für Tupel beliebiger Länge verwendet werden kann?

Ich denke, ich kann Vorlage Haskell oder ein externes Programm verwenden, um Definitionen im Voraus zu generieren, aber nicht auf Abruf wie C++ - Vorlagen generieren.

-- Sets equipped with the (is_subset) operation are an example of a 
-- partial order. 
-- 
-- {} < {a}  less than 
-- {} = {}  equal to 
-- {a, b} > {b} greater than 
-- {a} ~ {b}  incomparable 
-- 
-- in order to define a partial order we need a definition of (<=) 

data PartialOrdering = POLessThan | POGreaterThan | POEqual | POIncomparable deriving (Eq, Show) 

class PartialOrder a where 
    lessThanEq :: a -> a -> Bool 

instance PartialOrder PartialOrdering where 
    lessThanEq POIncomparable _ = False 
    lessThanEq _ POIncomparable = False 

    -- with incomparables dealt with... 
    lessThanEq POLessThan _ = True 

    lessThanEq POEqual POLessThan = False 
    lessThanEq POEqual _ = True 

    lessThanEq POGreaterThan POGreaterThan = True 
    lessThanEq POGreaterThan _ = False 


-- note this is different from the semantics for Ord applied to tuples, 
-- which uses lexicographic ordering. 
-- 
-- (a,b) is less than or equal to (c,d) iff 
-- a <= b and c <= d 

-- 2 element tuple 
instance (PartialOrder a, PartialOrder b) => PartialOrder (a, b) where 
    lessThanEq (a,b) (c,d) = (lessThanEq a c) && (lessThanEq b d) 

-- 3 element tuple 
instance (PartialOrder a, PartialOrder b, PartialOrder c) => PartialOrder (a, b, c) where 
    lessThanEq (a,b,c) (d,e,f) = (lessThanEq a d) && (lessThanEq b e) && (lessThanEq c f) 

-- 4 element tuple 
instance (PartialOrder a, PartialOrder b, PartialOrder c, PartialOrder d) => PartialOrder (a, b, c, d) where 
    lessThanEq (a,b,c,d) (e,f,g,h) = (lessThanEq a e) && (lessThanEq b f) && (lessThanEq c g) && (lessThanEq d h) 


-- etc. 


main = putStrLn "hi" 
+4

nur ein Vorschlag : Warum Sie nicht über eine Länge indizierte Liste verwenden? Solche Datentypen sind isomorph zu Tupeln. Es ist einfach, es mit GADTs zu definieren. –

Antwort

4

Die Tupeltypen in Haskell haben nicht wirklich ein Bewusstsein voneinander. Glücklicherweise können Sie Ihr Problem lösen, indem Sie GHC.Generics verwenden. Dann können Sie Ihre PartialOrder Klasse für beliebigen Produkttyp, nicht nur Tupel ableiten.

{-# LANGUAGE TypeOperators, DefaultSignatures, FlexibleContexts, 
      StandaloneDeriving, DeriveAnyClass 
    #-} 

import GHC.Generics 
import Data.Function (on) 

data PartialOrdering 
    = POLessThan | POGreaterThan | POEqual | POIncomparable deriving (Eq, Show) 

class PartialOrder a where 
    lessThanEq :: a -> a -> Bool 

    default lessThanEq :: (Generic a, GPartialOrder (Rep a)) => a -> a -> Bool 
    lessThanEq = gLessThanEq `on` from 


-- | Helper generic version of your class 
class GPartialOrder f where 
    gLessThanEq :: f a -> f a -> Bool 

-- | Product types 
instance (GPartialOrder a, GPartialOrder b) => GPartialOrder (a :*: b) where 
    gLessThanEq (a1 :*: b1) (a2 :*: b2) = gLessThanEq a1 a2 && gLessThanEq b1 b2 

-- | Unary type (empty product) 
instance GPartialOrder U1 where 
    gLessThanEq U1 U1 = True 

-- | Meta information on type 
instance (GPartialOrder a) => GPartialOrder (M1 i c a) where 
    gLessThanEq (M1 a1) (M1 a2) = gLessThanEq a1 a2 

-- | Single type 
instance (PartialOrder a) => GPartialOrder (K1 i a) where 
    gLessThanEq (K1 x1) (K1 x2) = lessThanEq x1 x2 

Mit allem, was einzurichten, kann man automatisch die Klasse abgeleitet werden (mit -XDeriveAnyClass aktiviert), wenn man Generic leitet (die mit -XDeriveGeneric getan werden kann). Tupel-Typen sind bereits Instanzen von Generika. Mit -XStandaloneDeriving können Sie Instanzen von Partial Order retro-aktiv ableiten. Alle unten Arbeit

deriving instance (PartialOrder a, PartialOrder b) => PartialOrder (a,b) 
deriving instance (PartialOrder a, PartialOrder b, PartialOrder c) => PartialOrder (a,b,c) 
-- and so on... 

data MyProduct a b = MyProduct a b deriving (Generic, PartialOrder) 

Danach können Sie Ihre Klasse verwenden, wie erwartet:

ghci> (POLessThan, POLessThan) `lessThanEq` (POEqual, POEqual) 
True 
ghci> (POLessThan, POEqual) `lessThanEq` (POEqual, POEqual) 
True 
ghci> (POLessThan, POEqual) `lessThanEq` (POEqual, POLessThan) 
False 
Verwandte Themen