尝试使用幺半群函子的应用实例

sim*_*uff 1 haskell applicative monoids

我正在学习 Haskell 并尝试从《Haskell 编程从第一原理》一书中进行练习,并且我正在尝试编写 Pair 类型的应用程序

 data Pair a = Pair a a deriving Show
Run Code Online (Sandbox Code Playgroud)

我在网上看到了一些其他示例,但我正在尝试一些不同的应用函子,我正在尝试利用这种类型的幺半群结构。这是我所拥有的

data Pair a = Pair a a deriving (Show, Eq)

instance Functor Pair where
     fmap f (Pair x y) = Pair (f x) (f y)

instance Semigroup a => Semigroup (Pair a) where
    (Pair x y) <> (Pair x' y') = Pair (x <> x') (y <> y')

instance Applicative Pair where
    pure x = Pair x x 
    (Pair f g) <*> p = fmap f p <> fmap g p
Run Code Online (Sandbox Code Playgroud)

不幸的是这不会编译:

* No instance for (Semigroup b) arising from a use of `<>'
  Possible fix:
    add (Semigroup b) to the context of
      the type signature for:
        (<*>) :: forall a b. Pair (a -> b) -> Pair a -> Pair b
* In the expression: fmap f p <> fmap g p
  In an equation for `<*>': (Pair f g) <*> p = fmap f p <> fmap g p
  In the instance declaration for `Applicative Pair'
Run Code Online (Sandbox Code Playgroud)

这就是我的堆栈;我不明白如何将类型类约束添加到 Applicative 定义中,我认为创建 Semigroup 的类型 Pair 实例就足够了。

我见过的其他解决方案就像

Pair (f g) <*> Pair x y = Pair (f x) (g y)
Run Code Online (Sandbox Code Playgroud)

但这些解决方案不利用 Pair 类型的幺半群部分

是否有可能以我没有尝试的方式使其适用?

lef*_*out 5

虽然这确实Applicative是代表幺半群函子的类(特别是,Hask内函子是幺半群的),但不幸的是,Allen 和 Moronuki 以一种似乎表明 和Monoid类之间存在直接关系的方式呈现了这一点Applicative一般来说,不存在这种关系!(该Writer类型确实Applicative根据Monoid类定义了一个特定实例,但这是一种极其特殊的情况。)
\n这在另一个 SO 问题上引发了相当广泛的讨论

\n\n

\xe2\x80\x9cmonoidal\xe2\x80\x9d in \xe2\x80\x9cmonoidal functor\xe2\x80\x9d 指的是类别对象上的幺半结构,即 Haskell 类型上的。也就是说,您可以将任意两种类型组合为元组类型。这本身与类没有任何关系Monoid,类是关于组合两个值的组合为相同类型的值。

\n\n

Pair确实允许Applicative实例,但您不能将其基于Semigroup实例,尽管定义实际上看起来非常相似:

\n\n
instance Applicative Pair where\n  pure x = Pair x x\n  Pair f g <*> Pair p q = Pair (f p) (g q)\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是,您现在可以根据此Semigroup定义实例

\n\n
instance Semigroup a => Semigroup (Pair a) where\n  (<>) = liftA2 (<>)\n
Run Code Online (Sandbox Code Playgroud)\n\n

这确实是任何Semigroup一个有效的例子,但它通常不是您想要的定义(通常,容器有一个自然的组合操作,永远不会触及所包含的元素,例如列表串联)。

\n