这个Haskell类型的推理是在行动还是其他什么?

Aky*_*Aky 8 haskell type-inference

我正在阅读在线LYAH书(该链接将直接带您到我的问题所涉及的部分).

作者定义了二叉树数据类型,并展示了如何通过实现foldMap函数将其作为Foldable类型的实例(在Data.Foldable中定义):

import Data.Monoid
import qualified Data.Foldable as F

data Tree a = Empty | Node a (Tree a) (Tree a) deriving (Show, Read, Eq)

instance F.Foldable Tree where  
  foldMap f Empty = mempty  
  foldMap f (Node x l r) = F.foldMap f l `mappend`  
                           f x           `mappend`  
                           F.foldMap f r 
Run Code Online (Sandbox Code Playgroud)

foldMap的类型声明如下:

F.foldMap :: (Monoid m, F.Foldable t) => (a -> m) -> t a -> m
Run Code Online (Sandbox Code Playgroud)

所以它需要一个函数,它接受一个类型为"a"的实例并返回一个monoid.

现在作为示例,作者创建一个Tree实例

    testTree = Node 5  
                 (Node 3  
                    (Node 1 Empty Empty)  
                    (Node 6 Empty Empty)  
                 )  
                 (Node 9  
                    (Node 8 Empty Empty)  
                    (Node 10 Empty Empty)  
                 )  
Run Code Online (Sandbox Code Playgroud)

并执行以下折叠(为可折叠类型定义):

F.foldl (+) 0 testTree -- the answer is 42 (sum of the Node Integers)
Run Code Online (Sandbox Code Playgroud)

我的问题是,Haskell如何计算出Integer类型的添加 - 查询Haskell的testTree类型给出Tree [Integer] - 可以被视为一个monoid操作(如果我的术语是正确的)?

(我自己尝试回答:作者在本节前几段描述了如何将Num类型以两种不同的方式解释为Monoid类型;通过(+)和(*)将它们包装到SumProduct类型中作为mappend函数,0和1分别作为mempty元素.( a)中的"a"的类型以某种方式被推断为属于Sum类型(Haskell根据上下文不同地解释数值的方式)或它完全是另一回事吗?]

C. *_*ann 12

我的问题是,Haskell如何计算出Integer类型的添加 - 查询Haskell的testTree类型给出Tree [Integer] - 可以被视为一个monoid操作(如果我的术语是正确的)?

它不能!事实上,没有Monoid实例Integer.

现在,不要误解我的意思 - 整数一个幺半群.然而,它们在乘法中也是一个幺半群,并且Haskell无法知道使用哪个,因此newtype包装器.

但是......这一切都不会发生在这里.继续...

(我自己尝试回答:作者在本节前几段描述了如何将Num类型以两种不同的方式解释为Monoid类型;通过(+)和(*)将它们包装到Sum和Product类型中作为mappend函数,0和1分别作为mempty元素.(树a)中的"a"的类型以某种方式被推断为属于​​Sum类型(Haskell根据上下文不同地解释数值的方式)或它完全是另一回事吗?]

不错的猜测,但是那种推断(Sum根据你给出的参数找到实例)超出了Haskell可以为你做的事情.

这里有两个关键点 - 首先,Monoid约束仅用于某些功能,而不是一般的折叠.特别是,foldl实际上根本不需要Monoid实例,因为您提供二进制操作和初始值供它使用.

第二点是我怀疑你真正追求的 - 它是如何创建一个foldl不需要a 的泛型Monoid,当你定义的是什么时foldMap,它是什么?要回答这个问题,我们可以简单地看一下默认实现foldl:

foldl :: (a -> b -> a) -> a -> t b -> a
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
Run Code Online (Sandbox Code Playgroud)

这里Endo是另一个newtype包装器,特别是对于a -> a赋予Monoid组合的函数,id作为标识,同时Dual是一个反转a的方向的包装器Monoid.所以Monoid它实际上在这里使用的是它可以粘合使用(+)与函数组合,然后将结果应用于种子值.

  • 很好的解释.`appEndo`听起来像是哈利波特的咒语. (3认同)
  • @Dan Burton:的确如此.你也不是[发表评论]的唯一人(http://contemplatecode.blogspot.com/2011/04/haskell-weekly-news-issue-176.html). (2认同)

por*_*ges 5

幺半群在这里实际上并没有使用.最后一行是使用F.foldl哪个有签名F.Foldable t => (a -> b -> a) -> a -> t b -> a.基本上你通过提供(+)和0来'手动'使用一个monoid.

如果你想使用'隐式'的monoid,你可以使用F.fold(有签名(F.Foldable t, Monoid m) -> t m -> m).在这种情况下,如果你尝试它,你会得到:

*Main> F.fold testTree

<interactive>:1:1:
    No instance for (Monoid Integer)
      arising from a use of `F.fold'
    Possible fix: add an instance declaration for (Monoid Integer)
    In the expression: F.fold testTree
    In an equation for `it': it = F.fold testTree
*Main> :t F.foldl
Run Code Online (Sandbox Code Playgroud)

现在,GHCI抱怨说Integer没有Monoid实例.您必须通过包装整数来选择Sum或Product.为此,我们可以使用F.foldMap(签名(F.Foldable t, Monoid m) => (a -> m) -> t a -> m):

*Main> F.foldMap Sum testTree
Sum {getSum = 42}
Run Code Online (Sandbox Code Playgroud)