操作参数的顺序以键入构造函数

时间:2010-02-25 17:07:41

标签: haskell types typeclass

我写了这样的话:

instance Functor (Either e) where

   fmap _ (Left a) = Left a

   fmap f (Right b) = Right (f b)

如果我希望fmap仅在Left时更改值,我该怎么做?

我的意思是,我使用什么语法来表示我使用的是Either _ b类型而不是Either a _

3 个答案:

答案 0 :(得分:9)

不幸的是,我认为没有办法直接这样做。使用函数,您可以使用flip部分应用第二个参数,但这不适用于Either等类型构造函数。

最简单的事情可能是将它包装在newtype

newtype Mirror b a = Mirrored (Either a b)

instance Functor (Mirror e) where
    fmap _ (Mirrored (Right a)) = Mirrored $ Right a
    fmap f (Mirrored (Left b)) = Mirrored $ Left (f b)

使用newtype进行换行也是为单个类型创建多个实例的标准方法,例如SumProduct是数字类型Monoid的实例。否则,每种类型只能有一个实例。

此外,根据您想要做的事情,另一个选择是忽略Functor并定义您自己的类型类:

class Bifunctor f where
    bimap :: (a -> c) -> (b -> d) -> f a b -> f c d

instance Bifunctor Either where
    bimap f _ (Left a)  = Left  $ f a
    bimap _ g (Right b) = Right $ g b

instance Bifunctor (,) where
    bimap f g (a, b) = (f a, g b)

显然,该课程的乐趣是普通Functor的两倍。当然,你不能轻易地制作Monad实例。

答案 1 :(得分:4)

您无法直接创建您要查找的实例。

为了使类型推断和类型类起作用,对类型中参数的排序存在一定的位置偏差。已经证明,如果我们在实例化类型类时允许对参数进行任意重新排序,那么这种类型推断就会变得棘手。

您可以使用Bifunctor类来分别映射两个参数。

class Bifunctor f where
    bimap :: (a -> b) -> (c -> d) -> f a c -> f b d
    first :: (a -> b) -> f a c -> f b c
    second :: (c -> d) -> f a c -> f a d

    first f = bimap f id
    second = bimap id

instance Bifunctor Either where
    bimap f _ (Left a) = Left (f a)
    bimap _ g (Right b) = Right (g b)

instance Bifunctor (,) where
    bimap f g (a,b) = (f a, g b)

或者您可以使用Flip组合器,如:

newtype Flip f a b = Flip { unFlip :: f b a }

这两种广义版本都可以在hackage类别附件中找到。后者甚至包含Functor (Flip Either a)的实例,因为EitherBifunctor。 (我应该解决这个问题,只需要PFunctor

最终,类型构造函数中的参数顺序对于确定可以实例化的类很重要。您可能需要使用newtype包装器(如上面的Flip)将参数放在需要它们的位置以限定构造另一个类型类的实例。这是我们为类型类约束推断所支付的价格。

答案 2 :(得分:3)

你基本上需要在类型上使用'翻转'组合器。正如camccann所说,反转顺序的newtype包装器应该可以工作。请注意,您不能使用“类型”同义词,因为它们可能未部分应用。