首页 > 解决方案 > Applicative 风格中的动作之间的交流

问题描述

Haskell 设计模式,写了这个例子:

(+) <$> Nothing <*> Just 10 <*> Just 20

证明应用风格的动作之间的交流有限。

问题是我没有让这个例子在 ghci 中编译,错误:

<interactive>:28: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) => Maybe b

标签: haskellghciapplicative

解决方案


这个例子是错误的。这些工作中的任何一个:

Prelude> (+) <$> Nothing <*> Just 10
Nothing
Prelude> (+) <$> Just 10 <*> Just 20
Just 30
Prelude> (+) <$> Just 20 <*> Nothing
Nothing

但给定的没有,因为(+)只有两个参数。

使用它作为(+)<$>_<*>_<*>_意味着编译器尝试推断(+)具有三个参数的类型。好吧,这并不是完全不可想象的,因为 Haskell 多参数函数实际上是一个参数的函数产生另一个参数的函数产生......所以,如果你用函数实例化自己,
“双参数函数”原则上a -> a -> a 可以a有更多的参数类型。说a ~ (Int -> Int),那么

(+) :: Num (Int -> Int) => (Int -> Int) -> (Int -> Int) -> Int -> Int
                           ┗━━━━━━━━━━┛    ┗━━━━━━━━━━┛    ┗━┛

瞧,三个论点。问题是,Int -> Int或者更一般地说a->b,它不是数字类型,即Num (a -> b)不是可以实现的约束,尽管可以想象有人可以编写这样的实例。

请注意,有时,约束是Monoid (a,b) 有意义的,GHC 确实允许它们,但您需要遵循建议并FlexibleContexts通过放在{-# LANGUAGE FlexibleContexts #-}源文件顶部或:set -XFlexibleContexts在 GHCi 中启用扩展。在您的示例中,这根本没有帮助。

要应用于(+)任意数量或参数,您可以将其折叠在列表上:

> :m +Data.List
> foldl1 (+) [0,10,20]
30

同样,您可以将 applicative-lifted 形式 ( liftA2 (+) p q ≡ (+)<$>p<*>q) 折叠到 Maybe-numbers 列表上:

> :m +Control.Applicative
> foldl1 (liftA2(+)) [Nothing, Just 10, Just 20]
Nothing
> foldl1 (liftA2(+)) [Just 0, Just 10, Just 20]
Just 30

推荐阅读