[英]Polymorphic function inside a type family
我试图在一个类型系列中定义一个函数,该函数在类型族中定义的幻象类型的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
非常感谢,
迈克尔
随着你给的签名, foo
是不可实现的MyChannelType
因为它声称能够产生Elem MyChannelType a
对任何a
类型。
如果您真正想要的是给定t
只应该有a
类型,您可以使用类型函数来表达:
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
实例)。
至于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
但是,我应该说,对于一些固定的类型集合来说,这是一个相当尴尬的事情。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.