【问题标题】:Polymorphic function inside a type family类型族内的多态函数
【发布时间】:2014-08-12 18:29:09
【问题描述】:

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

非常感谢,

迈克尔

【问题讨论】:

  • 这在您当前的签名中无法实现。为了帮助理解重新制定它的选项,您希望bar (foo Ch) :: [Int] 产生什么?
  • 或者,你想让它非法吗?

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


【解决方案1】:

根据您给出的签名,foo 对于 MyChannelType 是无法实现的,因为它声称能够为任何 a 类型生成 Elem MyChannelType 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 实例)。

【讨论】:

  • 嗨,Ganesh。谢谢你 - 我有点自豪,这正是我一直在做的事情!但是,我有一个更复杂的实例,它失败了 - 我已将详细信息添加到原始问题中。我知道这可能需要重新考虑,任何人都会感谢任何重新制定的建议。问候,迈克尔
【解决方案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

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

【讨论】:

  • 有趣——我什至没有意识到这是可以表达的。这是否等同于 Ganesh 提出的第二种解决方案?
  • 基本等价,但在实际使用的代码中看起来会有些不同。我非常喜欢ConstraintKinds,它们在MyChannelType 中对单一类型约束工作得更好,并且可以(但不一定)给出比MultiParamTypeClasses 更好的签名。对于像MyOtherType 这样的情况,Ganesh 的解决方案更好,因为它不需要如此尴尬的扔掉OtherType_Class
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-11-20
  • 1970-01-01
  • 2020-05-31
  • 1970-01-01
  • 2021-02-21
  • 2018-06-04
  • 1970-01-01
相关资源
最近更新 更多