Haskell:指定满足类型类的函数将采用满足约束的某种特定类型

时间:2016-10-02 05:09:15

标签: haskell types typeclass

我想我想有效地做这样的事情:

class (forall z. ListZip z) => PlaneZip p where... --functions using z excluded

简而言之,但更确切地说,我想拥有

class A p where ... --definition excluded
class C p where
  foo :: (A z) => p (z a) -> a
data Alpha a = ...definition excluded
data Beta a = ...definition excluded
instance A Alpha where --definition excluded
bar :: Beta (Alpha a) -> a
instance C Beta where
  foo = bar

这是不可能的,因为foo必须允许zA,而不是Alpha。如何使其成为可能,强制foo必须某些 A而不是任何 A

更详细地说,我有很多列表拉链,我想制作Comonad的实例。我没有创建列表拉链类型并围绕它创建大量包装器,而是决定创建一个类ListZip,并创建多个类型的实例。也就是说,

class (Functor z) => ListZip z where
    leftMv :: z a -> z a
    rightMv :: z a -> z a --further functions excluded

data ListZipInf a = ListZipInf ([a]) a ([a]) deriving (Show)
instance ListZip ListZipInf where... --functions excluded
data ListZipTorus a = ListZipTorus [a] a [a] deriving (Show)
instance ListZip ListZipTorus where.. --functions excluded

现在我想为2d列表制作类似的拉链 - 平面拉链 -

data PlaneZipInf a = PlaneZipInf (ListZipInf (ListZipInf a)) deriving (Show)
data PlaneZipTorus a = PlaneZipTorus (ListZipTorus (ListZipTorus a)) deriving (Show)

最后,我想制作一个类似的平面拉链类型类,这将允许我有一个默认的实现方式来拉出单个元素,这个列表拉链的'焦点',给出一种'解包方法' '平面拉链构造函数:

class PlaneZip p where
  unwrap :: (ListZip z) => p (z (z a)) -> z (z a)
  counit :: (ListZip z, Comonad z) => p (z (z a)) -> a
  counit p =
    let zs = unwrap p
      in extract $ extract zs

然而,这不起作用,特别是

instance PlaneZip PlaneZipInf where
    unwrap (PlaneZipInf zs) = zs

给我一​​个错误 - Could not deduce a ~ (ListZipInf (ListZipInf a)) 它期望一个适用于任何ListZip的函数,但我给它一个提供特定函数的函数。

我怎么能表达我希望unwrap采用p (z (z a))类型,其中z是某种类型,它是ListZip的一个实例,并生成一个{{} 1}}?

由于

2 个答案:

答案 0 :(得分:3)

我可能会误解你的问题,但在你的情况下,你似乎总是会从嵌套列表拉链中创建平面拉链。如果是这种情况,那么简单地执行以下操作似乎更为可取:

{-# LANGUAGE StandaloneDeriving, FlexibleContexts, UndecidableInstances #-}

data PlaneZip z a = PlaneZipInf (z (z a))
deriving instance (Show a, Show (z a), Show (z (z a))) => Show (PlaneZip z a)

type PlaneZipInf a = PlaneZip ListZipInf a
type PlaneZipTorus a = PlaneZip ListZipTorus a

unwrap :: PlaneZip z a -> z (z a)
unwrap (PlaneZip zs) = zs

counit :: Comonad z => PlaneZip z a -> a
counit p =
    let zs = unwrap p
      in extract $ extract zs

如果您确实要确保z中的PlaneZip z a 总是 ListZip,则可以使用以下GADT

{-# LANGUAGE GADTs #-}
data PlaneZip z a where
  PlaneZip :: ListZip z => z (z a) -> PlaneZip z a

如果情况并非如此,请随时回复 - 我相信还可以通过associated type families在此处完成其他工作,但上述解决方案要好得多。

答案 1 :(得分:1)

如何简单地使z成为该类的参数?

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class (ListZip z, Comonad z) => PlaneZip p z | p -> z where
  unwrap :: p (z (z a)) -> z (z a)
  counit :: p (z (z a)) -> a
  counit p =
    let zs = unwrap p
      in extract $ extract zs

instance PlaneZip PlaneZipInf ListZipInf where
    unwrap (PlaneZipInf zs) = zs

| p -> z语法是函数依赖;这意味着zp决定,因此instance PlaneZip PlaneZipInf SomeOtherType也是非法的。