如果类型参数的顺序错误,是否可以使类型成为类的实例?

时间:2011-11-04 05:50:06

标签: haskell

考虑以下类型:

data SomeType m a = SomeType (m Integer) [a]

我们可以使用以下代码轻松地将该类型作为Functor的实例:

instance Functor (SomeType m) where
  fmap f (SomeType m lst) = SomeType m (map f lst)

但是,如果交换了SomeType类型的参数:

data SomeType2 a m = SomeType2 (m Integer) [a]

然后上面的实例定义不起作用。

有没有办法让SomeType2成为Functor的一个实例?如果没有,那么haskell / ghc是否有任何新增功能可以实现?

4 个答案:

答案 0 :(得分:20)

我有偏见,但我认为这是一个很好的机会,可以使用Control.Newtype,这是一个小小的工具包,只需要安装新版本"程。

这是这笔交易。您想要翻转类型构造函数,以便在不同的参数中获得有趣的(例如)。定义新类型

 newtype Flip f x y = Flip (f y x)

并将其添加到Newtype

 instance Newtype (Flip f x y) (f y x) where
   pack = Flip
   unpack (Flip z) = z

Newtype类只是一个将newtypes映射到它们未经修饰的等价物的目录,提供了方便的套件,例如: op FlipFlip相反:您不需要记住您所谓的内容。

对于有问题的问题,我们现在可以做这样的事情:

 data Bif x y = BNil | BCons x y (Bif x y) deriving Show

这是一个两参数数据类型,恰好在两个参数中都是函数式的。 (或许,我们应该把它作为Bifunctor类的一个实例,但无论如何......)我们可以使它成为Functor两次:一次用于最后一个参数......

instance Functor (Bif x) where
  fmap f BNil = BNil
  fmap f (BCons x y b) = BCons x (f y) (fmap f b)

......一次是第一次:

instance Functor (Flip Bif y) where
  fmap f (Flip BNil) = Flip BNil
  fmap f (Flip (BCons x y b)) = Flip (BCons (f x) y (under Flip (fmap f) b))

其中under p f是一种简洁的方式来说op p . f . p

我告诉你没有谎言:让我们试试。

someBif :: Bif Int Char
someBif = BCons 1 'a' (BCons 2 'b' (BCons 3 'c' BNil))

然后我们得到

*Flip> fmap succ someBif
BCons 1 'b' (BCons 2 'c' (BCons 3 'd' BNil))
*Flip> under Flip (fmap succ) someBif
BCons 2 'a' (BCons 3 'b' (BCons 4 'c' BNil))

在这种情况下,有很多方法可以将同一件事看作是Functor,所以我们必须发出一些声音来说明我们的意思。但如果你对它有系统的话,噪音并不是那么大。

答案 1 :(得分:3)

这是不可能的,我认为不会很快。

类型参数的顺序非常重要。最后一个值是您要“包含”的值,以便与Functor等一起使用

我尝试通过定义一个类型同义词来解决这个问题,该类型同义词翻转了类型参数,然后使用TypeSynonymInstances扩展名,但它无法正常工作。

答案 2 :(得分:3)

您可以使用交换类型参数的newtype包装器。但是你得到一个新类型,并且必须区分代码中的原始类型和包装类型。

答案 3 :(得分:2)

你已经知道的愚蠢答案是:翻转你的参数!

对于GHC来说,在没有任何额外包装的情况下支持这种事情,你需要像Type-level lambdas这样的东西,它们很可能不会很快被添加。 (我很乐意被证明是错的)

instance Functor (\a -> SomeType2 a m) where
  -- fmap :: (a -> b) -> SomeType2 a m -> SomeType2 b m
  fmap f (SomeType2 lst m) = SomeType (map f lst) m

由于我们已经有了TypeSynonymInstances,我们可能希望PartiallyAppliedTypeSynonymInstances有时会比从未稍微好一点。

type SomeType3 m a = SomeType2 a m

instance Functor (SomeType m) where
  -- fmap :: (a -> b) -> SomeType3 m a -> SomeType3 m b
  -- or, synonymously:
  -- fmap :: (a -> b) -> SomeType2 a m -> SomeType2 a m
  fmap f (SomeType2 lst m) = SomeType (map f lst) m