2012-08-25 6 views
7

Ostatnie posty o HaskellDB, byłem zmotywowany do ponownego spojrzenia na HList. Ponieważ mamy teraz -XDataKinds w GHC, który faktycznie ma przykład heterogenicznych list, chciałem zbadać, jak wyglądają HLists z DataKinds. Do tej pory mam następujące:Czy można usunąć OverlappingInstances dla tej implementacji listy heterogenicznych list zabezpieczonych DataKinds?

{-# LANGUAGE FlexibleInstances #-} 
{-# LANGUAGE MultiParamTypeClasses #-} 
{-# LANGUAGE DataKinds #-} 
{-# LANGUAGE GADTs #-} 
{-# LANGUAGE OverlappingInstances #-} 
{-# LANGUAGE TypeFamilies #-} 
{-# LANGUAGE TypeOperators #-} 

import Data.Tagged 

data Record :: [*] -> * where 
    RNil :: Record '[] 
    (:*:) :: Tagged f (FieldV f) -> Record t -> Record (f ': t) 

type family FieldV a :: * 

emptyRecord = RNil 

(=:) :: (v ~ FieldV f) => f -> v -> Tagged f v 
f =: v = Tagged v 

class HasField x xs where 
    (=?) :: Record xs -> x -> FieldV x 

instance HasField x (x ': xs) where 
    (Tagged v :*: _) =? _ = v 

instance HasField x xs => HasField x (a ': xs) where 
    (_ :*: r) =? f = r =? f 

-------------------------------------------------------------------------------- 
data EmployeeName = EmployeeName 
type instance FieldV EmployeeName = String 

data EmployeeID = EmployeeID 
type instance FieldV EmployeeID = Int 

employee = (EmployeeName =: "James") 
     :*: ((EmployeeID =: 5) :*: RNil) 

employeeName = employee =? EmployeeName 
employeeId = employee =? EmployeeID 

To działa zgodnie z oczekiwaniami, ale moim celem tego projektu było, aby spróbować zrobić to bez klas typu jak najwięcej. Oto 2 pytania tutaj. Po pierwsze, czy można napisać (=?) (funkcja dostępu do rekordu pola) bez klasy typu? Jeśli nie, czy można go napisać bez nakładających się instancji?

Wyobrażam sobie, że nie jest możliwe moje pierwsze pytanie, ale może drugie może być możliwe. Chciałbym usłyszeć, co ludzie myślą!

+0

Ponieważ oryginalny dokument HList zdołał uciec tylko przy użyciu 'MultiParamTypeClasses' i' FunctionalDependencies', wyobrażałbym sobie tylko dodawanie (i używanie) 'DataKinds 'nie zmieni tego. –

+0

@ Ptharien'sFlame w dokumencie HList używa nakładających się instancji do wdrożenia TypeEq. Cała reszta może zostać wykonana przy użyciu TypeEq –

+0

@PhilipJF. Wszystko, czego potrzebujesz, to 'TypeFamilies' i' MultiParamTypeClasses', bez 'TypeEq' wymagany! –

Odpowiedz

2

Myślę, że odpowiedź na oba pytania jest kwalifikowana nie. Po prostu nie ma funkcji typu o postaci, która jest w istocie tym, co daje OverlappingInstances. Oleg zasugerował alternatywny mechanizm wykorzystujący TypeReps typu level, ale jeszcze go nie mamy. Ta odpowiedź jest kwalifikowany, ponieważ masz brzydkie „rozwiązania”, jak przy użyciu typeable

{-# LANGUAGE DataKinds, GADTs, DeriveDataTypeable, TypeFamilies, TypeOperators #-} 

import Data.Typeable 

type family FieldV a :: * 

data FieldOf f where 
    FieldOf :: FieldV f -> FieldOf f 

(=:) :: f -> FieldV f -> FieldOf f 
_ =: v = FieldOf v 

fromField :: FieldOf f -> FieldV f 
fromField (FieldOf v) = v 

data Record :: [*] -> * where 
    RNil :: Record '[] 
    (:*:) :: Typeable f => FieldOf f -> Record t -> Record (f ': t) 

data SameType a b where 
    Refl :: SameType a a 

useProof :: SameType a b -> a -> b 
useProof Refl a = a 

newtype SF a b = SF (SameType (FieldOf a) (FieldOf b)) 
sf1 :: FieldOf f -> SF f f 
sf1 _ = SF Refl 

targetType :: f -> Maybe (SF g f) 
targetType _ = Nothing 

(?=) :: Typeable a => Record xs -> a -> Maybe (FieldV a) 
RNil ?= _ = Nothing 
(x :*: xs) ?= a = case (gcast (sf1 x)) `asTypeOf` (targetType a) of 
        Nothing  -> xs ?= a 
        Just (SF y) -> Just . fromField $ useProof y x 

x =? v = case x ?= v of 
      Just x -> x 
      Nothing -> error "this implementation under uses the type system" 

data EmployeeName = EmployeeName deriving Typeable 
type instance FieldV EmployeeName = String 

data EmployeeID = EmployeeID deriving Typeable 
type instance FieldV EmployeeID = Int 

employee = (EmployeeName =: "James") 
     :*: ((EmployeeID =: 5) :*: RNil) 

employeeName = employee =? EmployeeName 
employeeId = employee =? EmployeeID 

to wyraźnie nie jest tak dobra jak w wersji opartej na typeclass. Ale jeśli jesteś w porządku z małym dynamicznym pisaniem ...

+0

Dzięki, wezmę to jako odpowiedź! Spośród wszystkich opcji nie jestem pewien, co lubię najbardziej. Nie mamy żadnych typów danych, nakładających się instancji lub musimy przynieść Typeable w ... – ocharles