我正在尝试在类型族中定义一个函数,该函数在类型族中定义的GADT本身的幻像类型上是多态的。
我的类型系列定义如下:
class Channel t where
data Elem t a :: *
foo :: t -> Elem t a
bar :: Elem t a -> [a]
我有一个如下实例:
data MyChannelType = Ch
instance Channel MyChannelType where
data Elem MyChannelType a where
MyConstructor :: Char -> Elem MyChannelType Char
foo _ = MyConstructor 'a'
bar (MyConstructor c) = repeat c
编译器抱怨说:
Couldn't match type ‘a’ with ‘Char’
‘a’ is a rigid type variable bound by
the type signature for foo :: MyChannelType -> Elem MyChannelType a
是否可以使用Rank2Types编写此函数或重新配置我的数据类型以启用它?
编辑:回应Ganesh要求的澄清
我希望foo (bar Ch) :: [Int]
是非法的。
我一直在使用完全 Ganesh建议的解决方案,但是我受到以下更复杂的例子的激励,其中它落空了;给出:
data MyOtherType = IntCh | StringCh
我有一个如下实例:
instance Channel MyOtherType where
data Elem MyOtherType a where
ElemInt :: Int -> Elem MyOtherType Int
ElemString :: String -> Elem MyOtherType String
foo IntCh = ElemInt 0
foo StringCh = ElemString "a"
bar (ElemInt i) = repeat i
bar (ElemString s) = repeat s
非常感谢,
迈克尔
答案 0 :(得分:5)
使用您已签名的签名,foo
无效MyChannelType
,因为它声称可以为任何Elem MyChannelType a
类型生成a
。
如果您真正想要的是给定a
应该只有一个t
类型,您可以使用类型函数来表达:
class Channel t where
data Elem t a :: *
type Contents t :: *
foo :: t -> Elem t (Contents t)
bar :: Elem t a -> [a]
然后添加
type Contents MyChannelType = Char
到实例。
为了回应您的编辑,我会将Channel
分为两类:
class Channel t where
data Elem t a :: *
bar :: Elem t a -> [a]
class Channel t => ChannelContents t a where
foo :: t -> Elem t a
然后,您可以使用以下内容定义MyOtherType
个实例
instance Channel MyOtherType where
data Elem MyOtherType a where
ElemInt :: Int -> Elem MyOtherType Int
ElemString :: String -> Elem MyOtherType String
bar (ElemInt i) = repeat i
bar (ElemString s) = repeat s
instance ChannelContents MyOtherType Int where
foo IntCh = ElemInt 0
instance ChannelContents MyOtherType String where
foo StringCh = ElemString "a"
您需要启用一些扩展程序:MultiParamTypeClasses
,TypeSynonymInstances
,FlexibleInstances
(后两者只是因为String
实例)。
答案 1 :(得分:2)
作为Ganesh解决方案的更通用的替代方案,您还可以将a
变量约束为整类类型(可能只是一个类型):
{-# LANGUAGE ConstraintKinds #-}
import GHC.Exts (Constraint)
class Channel t where
data Elem t a :: *
type ElemConstraint t a :: Constraint
foo :: ElemConstraint t a => t -> Elem t a
bar :: ElemConstraint t a => Elem t a -> [a]
instance Channel MyChannelType where
data Elem MyChannelType a where
MyConstructor :: Char -> Elem MyChannelType Char
type ElemConstraint t a = a ~ Char
foo _ = MyConstructor 'a'
bar (MyConstructor c) = repeat c
class OtherType_Class c where
mkOtherTypeElem :: c -> Elem MyOtherType c
evOtherTypeElem :: Elem MyOtherType c -> c
instance OtherType_Class Int where
mkOtherTypeElem = ElemInt
evOtherTypeElem (ElemInt i) = i
instance OtherType_Class String where
...
instance Channel MyOtherType where
data Elem MyOtherType a where
ElemInt :: Int -> Elem MyOtherType Int
ElemString :: String -> Elem MyOtherType String
type ElemConstraint MyOtherType a = OtherType_Class a
但是,我应该说,对于一些固定的类型集合来说,这是一个相当尴尬的事情。