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)并遇到类型约束。
那么,我的直觉正确吗?我终于想通了吗?或者我(在某种程度上)有根据的猜测仍然有误?
这只是没有正确应用替换规则的情况,并且忘记了这些替换中括号的重要性(尽管其中一些可以稍后删除)。该规则应表示为
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)
恐怕这并不像人们希望的那么漂亮。