我写了这样的东西:

instance Functor (Either e) where

   fmap _ (Left a) = Left a

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

如果我愿意,我该怎么做 fmap 仅在值时才更改该值 Left?

我的意思是,我用什么语法表示我使用类型 Either _ b 代替 Either a _?

有帮助吗?

解决方案

不幸的是,我认为没有办法直接做到这一点。使用功能可以使用 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 实例很容易。

其他提示

您无法直接寻找实例。

为了进行类型推理和类型类的工作,对类型中的参数排序存在一定的位置偏差。已经表明的是,如果我们在实例化类型类实例化时允许对参数进行任意重新排序,则该类型的推理变得棘手。

您可以使用 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 }

这两者的广义版本都可以在骇客上获得类别。后者甚至包括一个实例 Functor (Flip Either a) 因为 Either 是一个 Bifunctor. 。 (我可能应该解决这个问题 PFunctor)

最终,类型构造函数中的参数顺序对于确定可以实例化的类别很重要。您可能需要使用NewType包装器(例如 Flip 上面)提出要构建另一个类型类型的实例的参数。这是我们为类型类别约束推断的价格。

本质上,您需要在类型上进行“翻转”组合。正如Camccann所说,扭转订单的NewType包装器应起作用。请注意,您不能使用“类型”同义词,因为它们可能不会部分应用。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top