关于应用程序的多个参数不起作用?

Multiple arguments on applicatives not working?

所以我正在尝试学习 monad、函子和应用程序。我创建了以下名为 SometimesMaybe 的重命名镜像匹配。 (我这样做是为了了解这些东西)

data Sometimes a = Nope | Thing a deriving Show

instance Monad Sometimes where
    (Thing x) >>= f = f x
    Nope  >>= f = Nope
    return      = Thing

instance Applicative Sometimes where
      pure = Thing
      Nope <*> _ = Nope
      (Thing g) <*> mx = fmap g mx

instance Functor Sometimes where
     fmap _ Nope = Nope
     fmap g (Thing x) = Thing (g x)

所以当我执行以下操作时它会起作用:

pure (1+) <*> (Thing 1)
> Thing 2

pure (+) <*> (Thing 1) <*> (Thing 1)
> Thing 2

但是,如果我尝试三个加法,它就不起作用:

pure (+) <*> (Thing 1) <*> (pure 1) <*> (pure 1)

<interactive>:108:1: error:
    • Non type-variable argument in the constraint: Num (a -> b)
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall a b. (Num (a -> b), Num a) => Sometimes b

为什么这行不通?我希望前两个被应用,然后第三个被应用到前两个的结果。我的书谈到实施 fmap0, fmap1, fmap2... 是如何低效的,因此

... for functions with any desired number of arguments can be constructed in terms of two basic functions with the following types: pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b

并进一步说明:

A typical use of pure and <*> has the following form:

pure g <*> x1 <*> x2 <*> ... <*> xn

因此,我希望它能正常工作,但我在 Applicative 的 definitions/usage 中显然遗漏了一些东西。


我正在使用 Graham Hutton 的 Haskell SE 编程一书

这不起作用的原因是 (+) 求和两个数字,而不是三个。

您可以创建一个对三个数字求和的函数,例如:

pure (<b>\x y z -> x+y+z</b>) <*> (Thing 1) <*> (pure 1) <*> (pure 1)

这给了我们:

Prelude> pure (\x y z -> x+y+z) <*> (Thing 1) <*> (pure 1) <*> (pure 1)
Thing 3

Why doesn't this work? I would expect the first two to be applied and then the third to be applied to the result of the first two.

正是,但是应用了前两个之后,这就不再是一个函数了,而是一个Num a => Sometimes a。实际上,如果我们确定类型,我们会看到 Thing (+) :: Num a => Sometimes (a -> a -> a)Thing 1 :: Num b => Sometimes b,这意味着 Thing (+) <*> Thing 1 的类型为 Num a => Sometimes (a -> a).

然后我们判断Thing (+) <*> Thing 1 <*> Thing 1的类型,因为Thing (+) <*> Thing 1的类型是Num a => Sometimes (a -> a),而最后的Thing 1的类型是Num c => Sometimes c,也就是说Thing (+) <*> Thing 1 <*> Thing 1 具有类型 Num a => Sometimes a,但这不是一个函数,除非有一个 Num 类型是一个函数,这就是错误所说的。