2017-06-21 3 views
1

Was ich grob will, ist dies:Extensible Aufzeichnungen (glaube ich)

data A = ... 
data B = ... 
data C = ... 

class HasA t where 
    getA :: t -> A 

class HasB t where 
    getB :: t -> B 

class HasC t where 
    getC :: t -> C 

So kann ich etwas tun (Pseudo-Code folgt):

a :: A 
b :: B 

x = mkRecord { elemA a, elemB b } 
y = mkRecord { elemB b, elemA a } 

-- type of `x` == type of `y` 

natürlich nur die entsprechenden get Funktionen arbeiten , im obigen Fall getA und getB.

Ich möchte auch die folgenden Funktionen

slice :: Subset a b => a -> b 
slice x = -- just remove the bits of x that aren't in type b. 

add :: e -> a -> a ++ e 
add e x = -- add an element to the "record" (compile error if it's already there) 

ich so das Gefühl, kein neues Problem ist so vielleicht eine Lösung für diese bereits vorhanden ist. Beachten Sie, dass die Lösung nicht erweiterbar sein muss. Die Anzahl der Typen, mit denen ich umgehen muss, ist endlich und bekannt, aber natürlich und erweiterbar würde man nicht schaden.

Ich habe ein paar Pakete gefunden, die scheinen, in dem Bereich zu sein, was ich suche, nämlich HList und extensible (vielleicht erweiterbar ist besser, weil ich meine Datensätze ungeordnet haben will). Ich bin ein wenig in den Hackage-Dokumenten verloren gegangen, daher möchte ich nur einen Beispielcode (oder einen Link zu einem Beispielcode), der ungefähr das erreicht, wonach ich suche.

Antwort

1

Genau das ist HList ist gut für. Da ich jedoch nicht über das richtige Setup verfüge, um etwas mit dem Paket HList zu testen (und außerdem hat es more confusing data definitions), ist hier ein minimales Beispiel für HList, das singletons für die Typenliste verwendet.

{-# LANGUAGE DataKinds, TypeOperators, GADTs,TypeFamilies, UndecidableInstances, 
    PolyKinds, FlexibleInstances, MultiParamTypeClasses 
    #-} 

import Data.Singletons 
import Data.Promotion.Prelude.List 

data HList (l :: [*]) where 
    HNil :: HList '[] 
    HCons :: x -> HList xs -> HList (x ': xs) 

Die add Funktion ist die einfachste: es nur HCons ist:

add :: x -> HList xs -> HList (x ': xs) 
add = HCons 

Etwas interessanter ist die Kombination von zwei Datensätze:

-- Notice we are using `:++` from singletons 
combine :: HList xs -> HList ys -> HList (xs :++ ys) 
combine HNil xs = xs 
combine (x `HCons` xs) ys = x `HCons` (xs `combine` ys) 

Nun, für Ihre get Funktion, die Sie benötigen basierend auf der Typenliste zu versenden. Dazu benötigen Sie eine überlappende Typklasse.

class Has x xs where 
    get :: xs -> x 

instance {-# OVERLAPS #-} Has x (HList (x ': xs)) where 
    get (x `HCons` _) = x 

instance Has x (HList xs) => Has x (HList (y ': xs)) where 
    get (_ `HCons` xs) = get xs 

Schließlich können wir Has verwenden eine ähnliche Subset Klasse zu definieren. Gleiche Idee wie zuvor.

class Subset ys xs where 
    slice :: xs -> ys 

instance Subset (HList '[]) (HList xs) where 
    slice _ = HNil 

instance (Get y (HList xs), Subset (HList ys) (HList xs)) => 
      Subset (HList (y ': ys)) (HList xs) where 
    slice xs = get xs `HCons` slice xs 

Wie Sie in Pars erwähnen, ist die einfache HList Form nur ein von jeder Art von Feld nicht gewährleisten haben (so gibt get nur das erste Feld, um den Rest zu ignorieren). Wenn Sie die Eindeutigkeit möchten, können Sie dem Konstruktor HList einfach eine Einschränkung hinzufügen.

data Record (l :: [*]) where 
    Nil :: Record '[] 
    Cons :: (NotElem x xs ~ 'True) => x -> Record xs -> Record (x ': xs) 

jedoch definieren Subset mit Record sieht aus wie es einige Beweise beinhaltet. :)

Verwandte Themen