类型分类百科中应用语的构成规律

mel*_*ton 3 haskell applicative

我正在阅读Typeclassopedia,但在应用程序部分遇到了麻烦。我想我(有点)已经弄清楚了,但我想看看我的理解是否正确。

直到组合法出现之前,适用法则才有意义。我只是无法解析其右侧:

u <*> (v <*> w) = pure (.) <*> u <*> v <*> w
Run Code Online (Sandbox Code Playgroud)

因此,我启动了 GHCI 并进行了一些实验。

Prelude> pure (.) <*> Just (+1) <*> Just (+2) <*> Just 34
Just 37
Run Code Online (Sandbox Code Playgroud)

所以这验证了规律,但我还是不明白。我尝试了一些变化,看看是否可以获得一些见解:

Prelude> pure (.) <*> Just (+1) <*> Just (+2) <*> Just (+3) <*> Just 34

<interactive>:26:1: error:
    * Non type-variable argument in the constraint: Num (b -> b)
      (Use FlexibleContexts to permit this)
    * When checking the inferred type
        it :: forall b. (Num (b -> b), Num b) => Maybe b
Run Code Online (Sandbox Code Playgroud)

那么,组合适用于两个功能,但不适用于三个功能?我不明白为什么。

然后我验证了它的<*>工作原理,就像我想象的那样,对于一个简单的表达式来说:

Prelude> Just (+1) <*> Just 1
Just 2
Run Code Online (Sandbox Code Playgroud)

但是,以下方法不起作用:

Prelude>  Just (+1) <*> Just (+2) <*> Just 34

<interactive>:15:2: error:
    * Non type-variable argument in the constraint: Num (b -> b)
      (Use FlexibleContexts to permit this)
    * When checking the inferred type
        it :: forall b. (Num (b -> b), Num b) => Maybe b
Run Code Online (Sandbox Code Playgroud)

因此,应用与组合不同。我原以为这Just (+2) <*> Just 34会导致Just 36并且Just (+1) <*> Just 36会导致Just 37。组合运算符 ,(.)是必需的,但它仅适用于两个函数。我只是不明白为什么需要它或者它是如何工作的。

而且,为了向水中投入更多污垢,我尝试了以下操作:

Prelude> Just (,) <*> Just 2
Run Code Online (Sandbox Code Playgroud)

失败是因为没有Show结果的实例。所以我尝试:

Prelude> :t Just (,) <*> Just 2
Just (,) <*> Just 2 :: Num a => Maybe (b -> (a, b))
Run Code Online (Sandbox Code Playgroud)

这让我有了一点感悟。我需要传递第二个值才能返回tuple。我试过:

Prelude> Just (,) <*> Just 2 Just 34
Run Code Online (Sandbox Code Playgroud)

但这失败了,错误消息确实没有帮助我找出错误所在。因此,查看上述代码的类型,我意识到它与我输入的代码相同Just (2, )(或者类似的东西,无论如何)。所以,我尝试过:

Prelude> Just (,) <*> Just (2) <*> Just 34
Just (2,34)
Run Code Online (Sandbox Code Playgroud)

我真的很惊讶这有效。但在这种惊讶中产生了理解的萌芽(我认为)。

我回到(<*>)Typeclassopedia 中的定义,发现它被定义为关联。在此之前我什至没有考虑过关联性。所以上面的表达式实际上会计算出这样的结果:

Prelude> Just (,) <*> Just (2) <*> Just 34
-- Apply the 2 as the first parameter of (,) leaving a function
-- that takes the second parameter
Just (2,) <*> Just 34
-- Now apply the 34 as the parameter to the resulting function
Just (2,34)
Run Code Online (Sandbox Code Playgroud)

如果这是正确的,那么有效的组合律示例的计算结果如下:

Prelude> pure (.) <*> Just (+1) <*> Just (+2) <*> Just 34
pure (+1 . ) <*> Just (+2) <*> Just 34
pure (+1 . +2) <*> Just 34
Just 37
Run Code Online (Sandbox Code Playgroud)

这也解释了为什么三个函数不能这样组合:

Prelude> pure (.) <*> Just (+1) <*> Just (+2) <*> Just (+3) <*> Just 34
pure (+1 . ) <*> Just (+2) <*> Just (+3) <*> Just 34
pure (+1 . +2) <*> Just (+3) <*> Just 34
Run Code Online (Sandbox Code Playgroud)

并且,此时我们有一个组合函数,需要Num类型类的实例,但实际上向它传递了一个函数,但失败了。

以类似的方式:

Prelude>  Just (+1) <*> Just (+2) <*> Just 34
Run Code Online (Sandbox Code Playgroud)

失败,因为<*>不是组合,而是尝试将一个函数(+2)应用于另一个函数,(+1)并遇到类型约束。

那么,我的直觉正确吗?我终于想通了吗?或者我(在某种程度上)有根据的猜测仍然有误?

bhe*_*ilr 5

这只是没有正确应用替换规则的情况,并且忘记了这些替换中括号的重要性(尽管其中一些可以稍后删除)。该规则应表示为

u <*> (v <*> w) == (pure (.) <*> u <*> v) <*> w
Run Code Online (Sandbox Code Playgroud)

然后

Just (+1) <*> (Just (+2) <*> Just 34)
    -- u = Just (+1)
    -- v = Just (+2)
    -- w = Just 34
 => (pure (.) <*> u <*> v) <*> w
 => (pure (.) <*> Just (+1) <*> Just (+2)) <*> Just 34
Run Code Online (Sandbox Code Playgroud)

要在此基础上添加另一层,您必须使用另一个pure (.)

Just (+3) <*> (Just (+1) <*> (Just (+2) <*> Just 34))
    -- u = Just (+3)
    -- v = Just (+1)
    -- w = Just (+2) <*> Just 34
 => (pure (.) <*> u <*> v) <*> w
 => pure (.) <*> Just (+3) <*> Just (+1) <*> (Just (+2) <*> Just 34)
Run Code Online (Sandbox Code Playgroud)

如果您想从第一阶段开始就按照pure表格进行工作,那么您需要

Just (+3) <*> ((pure (.) <*> Just (+1) <*> Just (+2)) <*> Just 34)
    -- u = Just (+3)
    -- v = pure (.) <*> Just (+1) <*> Just (+2)
    -- w = Just 34
 => (pure (.) <*> u <*> v) <*> w
 => pure (.) <*> Just (+3) <*> (pure (.) <*> Just (+1) <*> Just (+2)) <*> Just 34
Run Code Online (Sandbox Code Playgroud)

恐怕这并不像人们希望的那么漂亮。

  • 这些括号很重要,如果你不小心,它们就会得到你的帮助。特别是当您使用结合律时,括号非常重要,因为操作顺序才是该定律真正要探索的内容。 (2认同)