【问题标题】:Why does a "let" statement force an "applicative do" block into requiring a monad constraint?为什么“let”语句强制“applicative do”块需要monad约束?
【发布时间】:2020-02-03 21:12:27
【问题描述】:

考虑这个例子:

{-# language ApplicativeDo #-}

module X where

data Tuple a b = Tuple a b deriving Show

instance Functor (Tuple a) where
    fmap f (Tuple x y) = Tuple x (f y)

instance Foldable (Tuple a) where
    foldr f z (Tuple _ y) = f y z

instance Traversable (Tuple a) where
    traverse f (Tuple x y) = do
        y' <- f y
        let t' = Tuple x y'
        return $ t'

看起来不错!但是没有:

[1 of 1] Compiling X                ( X.hs, interpreted )

X.hs:15:9: error:
    • Could not deduce (Monad f) arising from a do statement
      from the context: Applicative f
        bound by the type signature for:
                   traverse :: forall (f :: * -> *) a1 b.
                               Applicative f =>
                               (a1 -> f b) -> Tuple a a1 -> f (Tuple a b)
        at X.hs:14:5-12
      Possible fix:
        add (Monad f) to the context of
          the type signature for:
            traverse :: forall (f :: * -> *) a1 b.
                        Applicative f =>
                        (a1 -> f b) -> Tuple a a1 -> f (Tuple a b)
    • In a stmt of a 'do' block: y' <- f y
      In the expression:
        do y' <- f y
           let t' = Tuple x y'
           return $ t'
      In an equation for ‘traverse’:
          traverse f (Tuple x y)
            = do y' <- f y
                 let t' = ...
                 return $ t'
   |
15 |         y' <- f y
   |         ^^^^^^^^^
Failed, no modules loaded.

即使这样也失败了:

instance Traversable (Tuple a) where
    traverse f (Tuple x y) = do
        y' <- f y
        let unrelated = 1
        return $ Tuple x y'

因此,引入任何let 语句都会从“applicative do” 中删除“applicative”。为什么?

【问题讨论】:

  • 在阅读这里的答案之前,我认为这是因为调用了return。因此,对于未来的读者,我认为值得注意的是,即使使用 pure 而不是 return,这仍然会产生错误。

标签: haskell monads ghc applicative do-notation


【解决方案1】:

它会翻译成

let unrelated = 1 in return $ Tuple x y'

没有return &lt;something&gt;的形式,而applicative做requires the last statement to be a return or pure

一般来说,do 语句引发Monad 约束的规则如下。如果 do 表达式具有以下形式:

do p1 <- E1; ...; pn <- En; return E

如果p1...pn 定义的变量都没有在E1...En 中提及,并且p1...pn 都是变量或惰性模式,那么表达式将只需要Applicative。否则,表达式将需要Monad。该块可能会返回一个纯表达式E,具体取决于结果p1...pnreturnpure

注意:最后的语句必须完全匹配这些模式之一:

return E
return $ E
pure E
pure $ E

否则 GHC 无法将其识别为 return 语句,并且我们在上面看到的使用 &lt;$&gt; 的转换不适用。特别是,return . Just $ xlet x = e in return x 等细微变化将无法识别。

如果你看https://gitlab.haskell.org/ghc/ghc/wikis/applicative-do中关于脱糖的描述,它也不支持let

【讨论】:

    【解决方案2】:

    你想让这个去糖的应用表达式是什么? monadic 表达式是一系列链式作用域,因此 let 引入一个扩展到所有剩余作用域的绑定是有意义的,但对于 applicative,各种表达式不能真正相互依赖,因此没有作用域将let 脱糖是有意义的。

    【讨论】:

    • 原则上,do x1 &lt;- e1; ... ; xn &lt;- en ; let y = eY ; return eR 可以脱糖为do x1 &lt;- e1; ... ; xn &lt;- en ; return (let y = eY in eR) 并从那里转为应用符号(如果可能)。我们也可以对let y=eY; xi &lt;- ei 部分执行此操作,但我们需要要求eY 不使用x(i-1),...。需要对“什么可以在哪里使用”进行很多限制,这可能会令人困惑。因此,它看起来不是一个非常方便的功能。
    • 这与在整个do 表达式周围对let 脱糖不一样,因为被绑定的变量不能依赖于任何先前的绑定,也不能是被以下任何绑定使用?
    • 我想是的,除了最后的return,可以使用之前的绑定。
    • @chi 其实当我写这个例子的时候,我想,duh,当然应该把它脱糖成let ... in ... 表达式。我仍然不确定为什么不。
    • @IgnatInsarov 但是什么具体 let/in 表达?对于unrelated的稻草人来说,当然可以放在任何地方,但这不是很有趣。对于带有t' 的版本,没有明显的相同含义的let/in 表达式。最接近的是(\y' -&gt; let t' = Tuple x y' in t') &lt;$&gt; f y,我想这对于这个简单的案例来说似乎是合理的,但不能很好地概括,正如 chi 解释的那样。
    猜你喜欢
    • 1970-01-01
    • 2017-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 2015-02-16
    • 2012-03-08
    相关资源
    最近更新 更多