【问题标题】:haskell, monad, definition of bind, and weird pattern matching on an output value?haskell,monad,绑定的定义和输出值的奇怪模式匹配?
【发布时间】:2016-05-01 19:04:54
【问题描述】:

下面是来自another stack overflow question 答案的一些代码。我开始学习 Haskell 已经有几个星期了,但我还没有遇到这种特殊的语法,我在任何地方都找不到解释,甚至找不到它的定义。 所以,代码:

data Pair a = P a a

instance Functor Pair where
  fmap f (P x y) = P (f x) (f y)

instance Monad Pair where
  return x = P x x
  P a b >>= f = P x y
     where P x _ = f a
           P _ y = f b

我花了最后半个小时试图理解它的含义,或者我已经知道的东西解释了这个绑定(>>=)方法的定义。 GHCI 毫不费力地加载了它(它需要一个 Applicative 实例,但除此之外),所以它必须是非常允许的,即使我还没有理解它的开头。

通过模式匹配已经定义的数据构造函数的定义是什么意思? x 和 y 是什么,它们从何而来?

感谢任何回答这个问题的人。如果有人对这个问题有一个好的、具体的标题有一个好主意——考虑到我真的不明白它的语法含义,我很难找到一个。


@leftaroundabout 给了我我需要的信息,就是那段代码

P x y
  where P x _ = f a
        P _ y = f b

是一种“值拆箱”类型的模式匹配形式,而不是case of 式的面向选择的模式。我对_ 模式的存在感到困惑,因此我没有看到上述两个模式匹配,即x 然后y 的定义,因为它是在一种我以前从未见过的方式。

我知道我们可以这样写:

f :: foo -> (Int, Int)
...
i = let (a,b) = f x
    in a + b

但我不知道我们可以在这些“价值拆箱”的情况下使用(例如,这里是 ab,或者在困扰我的代码中的 xy),模式匹配的全部可能性,也就是说,至少,定义使用_ 来隔离我们不想要的部分,我们不想绑定到任何“标签”的值。

总之我不知道在上面的例子中,这个等式

P x _ = f a

实际上是x(f a)的结果进行模式匹配的定义,因此在效果上严格等价于

x = g (f a)
  where g (P t _) = t

我一直认为这是已经定义的数据构造函数P的定义。

【问题讨论】:

    标签: haskell pattern-matching bind monads


    【解决方案1】:

    这里没有“特殊语法”,只是普通的模式匹配。代码相当于

    pFst, pSnd :: Pair a -> a
    pFst (Pair x _) = x
    pSnd (Pair _ y) = y
    
    instance Monad Pair where
      return x = P x x
      P a b >>= f = P x y
         where x = pFst $ f a
               y = pSnd $ f b
    

    如果您内联 pFstpSnd,您会发现这直接导致了原始定义。

    如果您不相信,考虑一下(非递归)where 绑定可以替换为 lambda 抽象:

      P a b >>= f = (\(Pair x _) (Pair _ y) -> P x y)
                      (f a)      (f b)
    

    显然,lambda 也可以写成一个命名的局部函数:

      P a b >>= f = pFstAndPSnd (f a) (f b)
       where pFstAndPSnd (Pair x _) (Pair _ y) = P x y
    

    如果您已经看过代码,也许它会更容易混淆,因为它会寻找普通的元组:

    (>>=) :: (c,c) -> (c -> (d,d)) -> (d,d)
    (a,b) >>= f = (x,y)
      where (x,_) = f a
            (_,y) = f b
    

    显然没有以任何方式重新定义(,)构造函数,只是使用它对函数的元组结果进行模式匹配。


    好吧,也许这不是 显而易见的,因为您也可以执行 let 1+2=4 in 1+2 之类的操作,并将 4 作为结果。

    【讨论】:

    • 好吧,如果我翻译正确,这意味着,显然,可以反向进行模式匹配,从具有相同模式的变量中定义模式的一部分,所以我可以显然写:let (a,_) = (1,2); (_,b) = (3,4) in (a,b) == (1,4)。这种通过连续的“部分”模式匹配来定义值的能力,从其他变量中获取位,我只是忽略了这一点。在没有_ 的情况下我知道它,基本上,当然除了在定义函数时,但这里隐含地完全不同,你不觉得吗?
    • 顺便说一句,你写了pFst (Pair _ y) = y,但是是复制粘贴十六进制的受害者,因为它应该是pSnd。当然,谢谢。我试图编辑我之前的评论,但被拒绝了。
    • 我是 SOverflow 的新人,现在解决了我需要做一些特别的事情吗?
    • 评论并不意味着是持久的、可编辑的实体。如果不再正确,请删除评论。
    • 不,我打算编辑以添加我在第二条评论中写的内容。您的脚注示例很有趣,我是否必须理解 1 + 2 被视为非评估积分(因为懒惰),并且由于它的模式匹配为“单例”4,那么它是“盲目”评估的吗?并在let inin 之后被识别可能是因为Haskell 的图形缩减策略?
    【解决方案2】:

    P 确实是 Pair 的构造函数,所以 ab 将是这个的第一个和第二个元素(有点奇怪)pair(如果你说x = P 1 2x >>= f 然后在这里 a = 1b = 2 - 这正是 模式匹配

    我的猜测是你对函数的定义有问题。如果它有帮助,你可以写成:

    (>>=) (P a b) f = P x y
        where ...
    

    请参阅:就像您可以在参数之间使用运算符一样,您可以在那里定义它

    【讨论】:

    • 这不是我不理解的部分,而是你省略的部分,也就是 P x y 通过“反向”模式匹配定义的部分。我的意思当然是用我的术语。也就是应该通过像镜子一样从右侧取东西来填充左侧的那个(就像在 do-notation Monad“解封装”中模糊地说),而不是根据左侧来定义右侧= 符号,对于函数定义和 case of 模式匹配,在这种情况下当然是 -> 而不是 =
    • 它不是 defined - 这正是 pattern-matching 是什么 - 在定义函数 (>>=) 时,您将第一个参数与 @ 进行模式匹配987654336@ 并且因为Pair只有这个构造函数,这将捕获所有内容(所以不需要另一个) - 你应该从你在函数中使用列表的方式中看到这一点(你通常有一个模式/案例@ 987654338@ 和一个 f (x:xs) = ... - 对于列表的每个构造函数又是一个
    • 当然,您也可以使用 case ... of 构造来做到这一点 - 但这样读起来会更好,您不同意吗?
    • 你有点耳聋,但我再重复一遍:P a b没有问题。我对xy 的基于模式匹配的定义有问题,好吧,在绑定方法定义的= 之后的全局区域,而不是之前。我很清楚函数定义模式匹配。你真的相信经过几周的学习,任何人都会错过 Haskell 的关键点吗?正如你所说,我(任何人)应该已经理解了你反复尝试让我理解的内容,事实上,我确实已经了。
    • @trigone 不用担心,我不认为 Carsten 太冒犯了,你不会因为一句不礼貌的评论而被开除。更多的是对未来的一般建议:如果您在 StackOverflow 上发现了一些不合时宜的内容,请忽略它——发帖者可能针对的是您从未想过的问题的某个方面,但这对于 other 仍然有用i> 在未来找到该帖子并且实际上正在寻找其他方面的用户。
    猜你喜欢
    • 1970-01-01
    • 2018-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-09
    • 1970-01-01
    • 2013-07-01
    相关资源
    最近更新 更多