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


我写了这样的话:

instance Functor (Either e) where

   fmap _ (Left a) = Left a

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

如果我愿意,我该如何做同样的事情 fmap 只有当它改变时才改变它 Left

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


4919
2018-02-25 17:07


起源

有关: stackoverflow.com/questions/1827645  简而言之,Haskell现在不可能实现。 - ephemient
顺便说一句,这个问题的标题有点不清楚,但我不确定哪个更好。 - C. A. McCann
我提出了一个更精确的标题。 - Don Stewart
谢谢。我无法想出更好的东西。 - mik01aj


答案:


不幸的是,我认为没有办法直接这样做。有了你可以使用的功能 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 也是为单个类型创建多个实例的标准方法,例如 Sum 和 Product 作为的实例 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 很容易就是这样的例子。


8
2018-02-25 17:29



关于什么 newtype Flip t a b = Flip (t b a) 接着 instance Functor (Flip Either e)? - Norman Ramsey
它不起作用:“所有实例类型必须是(T a1 ... an)形式,其中a1 ... an是类型 变量“ - mik01aj
@ m01:它有效,但需要启用GHC语言扩展。在实践中,我要么(没有双关语意)按照Norman Ramsey建议或使用的方式去做 Bifunctor我的回答中的课程。过度专业化 Mirror 类型主要是为了说明这个想法。 - C. A. McCann
m01:调用您需要的扩展名 FlexibleInstances。 - Edward KMETT


您无法直接创建所需的实例。

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

你可以用一个 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) 因为 Either 是一个 Bifunctor。 (我应该解决这个问题,只需要一个 PFunctor

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


4
2018-03-02 19:06





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


3
2018-02-25 17:34