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

Applicative instance trying to use monoidal functors

我正在学习 Haskell 并尝试从书本 Haskell 编程中做练习,我正在尝试为 Pair 类型编写应用程序

 data Pair a = Pair a a deriving Show

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

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

不幸的是,这不会编译:

* 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'

这就是我的堆栈;我不知道如何将类型类约束添加到 Applicative 定义中,我认为制作 Semigroup 的类型对实例就足够了。

我看到的其他解决方案如

Pair (f g) <*> Pair x y = Pair (f x) (g y)

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

是否有可能按照我尝试的方式制作此应用程序?

我不认为 PairApplicative 你想要的样子,Applicative

(<*>) :: f (a -> b) -> f a -> f b

应该适用于第一位置的所有功能,而您想要

(<*>) :: Semigroup b => f (a -> b) -> f a -> f b.

如果 Pair 始终是 Semigroup(例如 MaybeList),您的推理是合理的,但您需要先决条件 Pair-containee 为 Semigroup.

虽然 Applicative 确实是 class 代表幺半群的函子(具体来说,Hask 内函子是幺半群的),Allen&Moronuki 不幸地在一个这似乎表明 MonoidApplicative class 之间存在直接关系。 一般来说,没有这种关系! (Writer 类型确实定义了一个基于 Monoid class 的特定 Applicative 实例,但这是一种极其特殊的情况。)
这产生了 .

“幺半群函子”中的“幺半群”指的是类别对象上的幺半群结构,即Haskell类型上的幺半群结构。也就是说,您可以将任意两种类型组合成一个元组类型。这本身与 Monoid class 没有任何关系,Monoid class 是关于将单个类型的两个 组合成相同类型的值。

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

instance Applicative Pair where
  pure x = Pair x x
  Pair f g <*> Pair p q = Pair (f p) (g q)

但是,您现在可以根据此:

定义 Semigroup 实例
instance Semigroup a => Semigroup (Pair a) where
  (<>) = liftA2 (<>)

这确实是 any 应用程序的有效 Semigroup 实例,但它通常不是您想要的定义(通常,容器具有自然组合操作从不触及包含的元素,例如列表连接)。

正确:Pair 无法按照您想要的方式成为 Applicative,因为 Applicative f 要求 f a "feel applicative-y" 任何 a,甚至非Semigroup a。考虑编写替代方案 class 并实施它:

class CApplicative f where
    type C f
    pure :: C f a => a -> f a
    app  :: C f b => f (a -> b) -> f a -> f b

instance CApplicative Pair where
    type C Pair = Semigroup
    pure x = Pair x x
    app (Pure f g) p = fmap f p <> fmap g p

这可以从基础 4.17.0.0 开始派生,其中包括过孔类型 GenericallyGenerically1。这将派生出与 leftaroundabout 所写的相同的实例:

{-# Language DeriveGeneric      #-}
{-# Language DerivingStrategies #-}
{-# Language DerivingVia        #-}

import Data.Monoid
import GHC.Generics

data Pair a = Pair a a
  deriving
  stock (Generic, Generic1)

  deriving (Semigroup, Monoid)
  via Generically (Pair a)

  deriving (Functor, Applicative)
  via Generically1 Pair

或者您可以通过 Applicative

提升 SemigroupMonoid
  deriving (Semigroup, Monoid, Num)
  via Ap Pair a