尝试使用单曲面仿函数的应用实例

时间:2018-08-03 11:29:02

标签: haskell applicative monoids

我正在学习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'

这是我的堆栈;我看不到如何向应用定义中添加类型类约束,我认为使Semigroup的类型对实例足够了。

我看到的其他解决方案就像

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

但是这些解决方案没有利用Pair类型的单曲面部分

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

3 个答案:

答案 0 :(得分:3)

尽管确实是Applicative代表单调函子的类(特别是 Hask 是单调函子的内函子),但是Allen&Moronuki不幸地提出了这一点,似乎暗示了它们之间的直接关系。 MonoidApplicative类。 通常没有这种关系! (Writer类型的确基于Applicative类定义了一个特定的Monoid实例,但这是一个非常特殊的情况。)
这产生了a rather extended discussion at another SO question

“类仿函数”中的“类”是指类别对象上的单项结构,即Haskell类型。即,您可以将任意两种类型组合为元组类型。这本身与Monoid类无关,它与将单个类型的两个组合为相同类型的值有关。

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实例,但通常不是您想要的定义(通常,容器具有自然的组合操作,永远不会触及所包含的元素,例如列表串联)。

答案 1 :(得分:1)

我不认为Pair是您想要的ApplicativeApplicative指出

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

应该将所有功能都放在第一位,而您想要

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

如果Pair始终是Semigroup(例如MaybeList),您的推理就很合理,但是您需要Pair的先决条件-被收容者为Semigroup

答案 2 :(得分:0)

正确:Pair不能以您想要的方式被设置为Applicative,因为Applicative f要求{em {1}}对任何 f a,甚至非a Semigroup个。考虑编写一个替代类并实现它:

a