类型族中的多态函数

时间:2014-08-12 03:31:38

标签: haskell typeclass type-families type-level-computation

我正在尝试在类型族中定义一个函数,该函数在类型族中定义的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

非常感谢,

迈克尔

2 个答案:

答案 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"

您需要启用一些扩展程序:MultiParamTypeClassesTypeSynonymInstancesFlexibleInstances(后两者只是因为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

但是,我应该说,对于一些固定的类型集合来说,这是一个相当尴尬的事情。