我试图了解其工作原理;在GHCi中:
foldMap (+3) (Just 5) :: Sum Int
产生结果
Sum {getSum = 8}
现在,foldMap的类型为
foldMap :: (Foldable t, Monoid m) => (a -> m) -> t a -> m
并且从该类型中,foldMap使用的函数签名与使用的函数(+3)签名不匹配:
(+3) :: Num a => a -> a
vs
f :: Monoid m => a -> m
如果我也尝试这样的事情:
foldMap _ (Just 5) :: Sum Int
---------------------------------------
<interactive>:113:9: error:
* Found hole: _ :: Integer -> Sum Int
* In the first argument of `foldMap', namely `_'
In the expression: foldMap _ (Just 5) :: Sum Int
....
这还表明期望的函数具有签名:: (Integer -> Sum Int)
,该函数与foldMap声明中的签名一致,但与上面使用的(+3)不一致吗?我对foldMap的理解是,它使用函数将Foldable实例的每个元素转换成Monoid,然后可以折叠成单个值。
我假设编译器推断出应该是什么类型(在上面明确声明的那一行中),但是我不明白的是编译器如何“调整”函数的类型签名(+3),以便第一行进行编译? / p>
答案 0 :(得分:6)
简而言之:由于instance Num a => Num (Sum a)
成立,因此您的5
被视为Sum Int
。
给出的a
是Num
类型,Sum a
也是Num
类型。实际上,我们在documentation中看到:
Num a => Num (Sum a)
现在Num
类型的n
具有函数fromInteger :: Integer -> n
,可以将Integer
转换为该数字类型n
。这样的想法是,如果您这样写5
,就隐式地写了fromInteger 5
之类的东西。
通过写作
foldMap (+3) (Just 5) :: Sum Int
使用foldMap :: (Foldable f, Monoid m) => (a -> m) -> t a -> m
,我们知道m
应该是Sum Int
,t ~ Maybe
,并且由于(+3) :: Num a => a -> a
,这意味着m ~ a ~ Sum Int
。因此,这意味着5
(来自Just 5
)和3
(来自(+3)
)是Sum Int
。您所写的5
因此被解释为Sum 5
。 3
也是如此。
现在我们知道我们实际上已经写过:
foldMap (+ (Sum 3)) (Just (Sum 5)) :: Sum Int
foldMap
会将结构的每个元素映射到一个Monoid(好了,它已经是一个Monoid,但是无论如何它将用3
对其进行递增),然后执行折叠。所以这意味着我们写了类似的东西:
(+ (Sum 3)) (Sum 5) <> mempty
对于Num n => Sum n
类型,mempty
是Sum 0
,这意味着我们写了:
(+ (Sum 3)) (Sum 5) <> (Sum 0)
,(<>)
的{{1}}是Sum
,因此表示表达式可以归结为:
(+)
可以评估为:
(+ (Sum 3)) (Sum 5) + (Sum 0)
或:
(Sum 5 + Sum 3) + Sum 0