【问题标题】:How function signature matches requested types函数签名如何匹配请求的类型
【发布时间】:2018-08-08 21:08:50
【问题描述】:

我试图了解这是如何工作的;在 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

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 -&gt; Sum Int),与 foldMap 声明中的签名一致,但与上面使用的 (+3) 不一致?我对 foldMap 的理解是它应用函数将 Foldable 实例的每个元素转换为 Monoid,然后可以折叠成单个值。

我假设编译器推断应该是什么类型(在上面的行中明确说明)但我不明白编译器如何“调整”函数的类型签名(+3)以便第一行编译?

【问题讨论】:

  • 好吧instance Num a =&gt; Num (Sum a) 成立,这意味着5Sum Int
  • 哎呀,“发现洞:_ :: Integer -&gt; Sum Int”令人难以置信的误导!它实际上应该类似于“找到洞:_ :: exists a. Num a *&gt; a -&gt; Sum Int”,但 existsforall 的对偶)和 (*&gt;)(=&gt;) 的对偶)在 Haskell 中不存在(还没有?)。默认选择a ~ Integer,但其他选择也是有效的,尤其是Sum Int,因此该孔的另一个有效类型是Sum Int -&gt; Sum Int——并且(+3)确实可以被赋予该类型。
  • @DanielWagner 我在禁用默认设置后得到Found hole: _ :: a0 -&gt; Sum Int。然后Ambiguous type variable ‘a0’ arising from the literal ‘5’ prevents the constraint ‘(Num a0)’ from being solved. 不太破旧。默认版本确实值得。
  • @chi 啊,很好,我试过-XNoMonomorphismRestriction,因为这是默认的通常 罪魁祸首,但没有帮助;我没想过要尝试default (),但是一旦我阅读了您的评论,很明显这是要走的路,而且错误的集合看起来要好得多!

标签: haskell type-inference


【解决方案1】:

简而言之:由于instance Num a =&gt; Num (Sum a) 成立,您的5 被视为Sum Int

鉴于aNum 类型,Sum a 也是Num 类型。事实上,在documentation 我们看到:

Num a => Num (Sum a)

现在Num 类型n 有一个函数fromInteger :: Integer -&gt; nInteger 转换为该数字类型n。这个想法是,如果你这样写5,你就隐含地写了fromInteger 5之类的东西。

通过写作

foldMap (+3) (Just 5) :: Sum Int

使用foldMap :: (Foldable f, Monoid m) =&gt; (a -&gt; m) -&gt; t a -&gt; m,我们因此知道m 应该是Sum Int,即t ~ Maybe,并且由于(+3) :: Num a =&gt; a -&gt; a,它意味着m ~ a ~ Sum Int。这意味着5(来自Just 5)和3(来自(+3))是Sum Ints。因此,您编写的5 被解释为Sum 53 也是如此。

所以现在我们知道我们实际上已经写了:

foldMap (+ (Sum 3)) (Just (Sum 5)) :: Sum Int

foldMap 将结构的每个元素映射到一个幺半群(它已经是一个幺半群,但无论如何它都会用3 递增它们),然后执行折叠。所以这意味着我们写了类似的东西:

(+ (Sum 3)) (Sum 5) <> mempty

对于Num n =&gt; Sum n 类型,memptySum 0,这意味着我们写道:

(+ (Sum 3)) (Sum 5) <> (Sum 0)

(&lt;&gt;)Sum(+),所以这意味着表达式归结为:

(+ (Sum 3)) (Sum 5) + (Sum 0)

可以评估为:

(Sum 5 + Sum 3) + Sum 0

或:

Sum 8

【讨论】:

  • 嘘!我不知道编译器会运行 fromInteger 函数,试图将 3 和 5 调整为允许签名匹配和编译的东西;现在可以理解了,谢谢!
  • @simkestuff 你真的不需要知道fromInteger 是编译器的工作方式(除非你正在编写自己的Num 实例),但你确实需要知道像 35 这样的数字文字可以解释为许多不同的类型。编译器选择的解释取决于您使用它们的上下文,因此不幸的是,当您更改代码以获取更多信息(如您的问题中的孔)时,您可能会更改数字的类型并获得误导性信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-04-23
  • 1970-01-01
  • 1970-01-01
  • 2021-01-10
  • 2018-01-12
  • 2011-12-19
  • 2021-07-19
相关资源
最近更新 更多