¿Es posible eliminar OverlappingInstances para esta implementación de lista heterogénea respaldada por DataKinds?

Con publicaciones recientes sobre HaskellDB, me motivó a buscar HList nuevamente. Como ahora tenemos -XDataKinds en GHC, que en realidad tiene un ejemplo de listas heterogéneas, quería investigar cómo se ven las listas HL con DataKinds. Hasta el momento tengo lo siguiente:

{-# 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

Esto funciona como se esperaba, pero mi objetivo en este proyecto era intentar hacerlo sin clases de tipos tanto como fuera posible. Así que hay 2 preguntas aquí. En primer lugar, ¿es posible escribir (=?) (la función de acceso de campo de registro) sin una clase de tipo? Si no, ¿se puede escribir sin instancias superpuestas?

Me imagino que no es posible para mi primera pregunta, pero tal vez la segunda sí lo sea. ¡Me encantaría saber qué piensa la gente!

preguntado el 25 de agosto de 12 a las 19:08

Dado que el documento original de HList logró salirse con la suya usando solo MultiParamTypeClasses y FunctionalDependencies, me imagino simplemente agregando (y usando) DataKinds no cambiaría eso. -

@Ptharien'sFlame, el artículo de HList usa instancias superpuestas para implementar TypeEq. Todo lo demás se puede hacer usando TypeEq -

@PhilipJF Entonces todo lo que necesitas es TypeFamilies y MultiParamTypeClassesEn TypeEq ¡requerido! -

1 Respuestas

Creo que la respuesta a ambas preguntas es calificada. no. Simplemente no puede tener una función de tipo de la forma

type family TypeEq a b :: Bool
type instance TypeEq a a = True
type instance TypeEq a b = False

que es esencialmente lo que te da OverlappingInstances. Oleg sugirió un mecanismo alternativo usando TypeReps de nivel de tipo, pero aún no lo tenemos. Esta respuesta es calificada, porque usted tiene feo "soluciones" como usar 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

esto claramente no es tan bueno como la versión basada en typeclass. Pero, si está de acuerdo con un poco de escritura dinámica...

Respondido 26 ago 12, 10:08

Gracias, lo tomaré como respuesta. De todas las opciones, no estoy seguro de cuál me gusta más. O no obtenemos tipos de datos, instancias superpuestas, o tenemos que traer Typeable en... - ocharles

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.