首页 > 解决方案 > 关于应用程序的多个参数不起作用?

问题描述

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

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...是如何低效的,因此

... 对于具有任意数量参数的函数,可以根据具有以下类型的两个基本函数来构造: pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b

并进一步说明:

pure和的典型用法<*>有以下形式:

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

因此,我希望它能够工作,但我显然在我的定义/使用中遗漏了一些东西Applicative


我正在使用 Graham Hutton 所著的《Haskell SE 编程》一书

标签: haskellmonadsapplicative

解决方案


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

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

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

这给了我们:

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

为什么这不起作用?我希望前两个被应用,然后第三个被应用到前两个的结果。

没错,但是在应用了前两个之后,这不再是一个函数,而是一个Num a => Sometimes a. 事实上,如果我们确定类型,我们会看到Thing (+) :: Num a => Sometimes (a -> a -> a)and Thing 1 :: Num b => Sometimes b,所以这意味着它Thing (+) <*> Thing 1有 type 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类型是函数,这就是错误的意思。


推荐阅读