我不确定我是否理解haskell中foldl函数的类型定义

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我不确定我是否理解haskell中foldl函数的类型定义相关的知识,希望对你有一定的参考价值。

当我问到foldl类型时,我看到的是:

*Main> :t foldl
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b

在这种情况下,什么是t a

我想这意味着该函数使用Foldable参数化a,但我甚至不确定语法。例如,为什么我不能用t a替换Foldable a

而且,奖金问题,如果我必须自己定义foldl,我会从基础案例开始

foldl f b [] = []

但是如果基本案例采用列表,那么接受Foldable就没有多大意义了。什么是我可以用作基本案例的“空可折叠”?

答案

Foldable被称为“类型类”。说Foldable t =>声明t必须实现Foldable的要求。然而,t仍然是t,并没有像Java中那样折叠成对Foldable接口的引用。这就是为什么你不能只有Foldable a

绝对查看https://hackage.haskell.org/package/base-4.10.1.0/docs/Data-Foldable.html以了解Foldable的要求以及可以获得哪些方法。

无论如何,如果你想使用Foldable的方法之一,那么要么使用像Foldable这样的已知Set类型:

import qualified Data.Set as S
import Data.Foldable

sumSet :: (Num n) => S.Set n -> n
sumSet ns = foldl' ( n acc -> n + acc ) 0 ns --make this pointfree if you want

或者您可以采用类型参数并将其约束为可折叠:

sumFld :: (Num n, Foldable f) => f n -> n --different signature
sumFld ns = foldl' ( n acc -> n + acc ) 0 ns --same implementation!

以下打印6,两次:

main :: IO ()
main = print (sumSet $ S.fromList [1, 2, 3]) >> print (sumFld $ S.fromList [1, 2, 3])
另一答案

我喜欢其他答案,这些答案简要讨论了类型和类之间的区别,因此以这种方式回答问题的前半部分。为了完成这个答案,我将非常简短地重申一下;但是请看其他答案以获得更长的解释,因为我想把大部分时间花在问题的后半部分。

所以:Foldable是类型类(类型集合)。 t类型的foldl游侠类型,而不是类型的集合,这解释了为什么它不能被Foldable取代。

但我认为这也是一个非常有趣的问题,其他答案没有解决:

如果我必须自己定义foldl,我将从基础案例开始

foldl f b [] = []

但是如果基本案例采用列表,那么接受Foldable就没有多大意义了。

如果你自己定义foldl,你将为特定类型创建一个Foldable实例。因为在实例中您知道涉及哪种特定类型,所以您可以为该类型编写基本案例,而无需了解Foldable的任何其他实例。在列表的Foldable实例中:

instance Foldable [] where
    foldl f b [] = b

...是完美的打字,因为我们知道t ~ []。另一方面,在另一种类型的实例中,我们当然必须使用不同的基本情况。例如:

import qualified Data.Set as S
instance Foldable S.Set where
    foldl f b s | S.null s = b

再次,这在instance区块内是可以的,因为那时我们有t ~ S.Set。我们可以(并且通常必须)编写仅适用于S.Sets的内容,并且不能与其他Foldable实例一起使用。

另一答案
foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b

在这种情况下,什么是t a

我想这意味着该函数正在使用Foldable参数化a [...]

这是完全正确的。但请注意,它是“用Foldable参数化的a”,而不是“用Foldable参数化的a”。正如hegel5000's answer解释的那样,当你在Foldable t的左边写=>时,你说的是类型(构造函数)t。用t a替换Foldable a有点像将名词放在名词应该是的位置。

继续你的奖金问题:foldl碰巧是Foldable类的方法。这意味着当你为一个类型编写一个Foldable实例时,你能够提供一个合适的foldl定义,你可以在其中使用该类型的特定特征(例如,[]是列表的构造函数 - 参见Daniel Wagner's answer)。

但是,如果foldl不是Foldable的方法,那么在实现它时你能用来处理t a论证的唯一方法就是FoldablefoldrfoldMap等)的(其他)方法,就像你一样除了它有一个t实例的事实之外,我不知道关于Foldable的任何其他事情。 (也许令人惊讶的是,有可能以这种方式实现foldl。这样的实现是作为foldl类中的Foldable方法的默认实现提供的,因此如果你不做,那么在编写实例时你不必自己实现它。不认为有必要。)

另一答案

(Foldable t) => ...的背景下,t是可折叠的类型。这方面的一个例子可能是列表类型[]

foldl的一些更具体的类型将是

foldl :: (b -> a -> b) -> b -> [a] -> b
foldl :: (b -> a -> b) -> b -> Tree a -> b

Foldable t => ...可以理解为t必须是Foldable。它不是应用于Foldable类型的类型构造函数t!因此Foldable t甚至不是一种类型,你不能在=>的右侧使用它。

以上是关于我不确定我是否理解haskell中foldl函数的类型定义的主要内容,如果未能解决你的问题,请参考以下文章

Haskell - 严格与非严格与 foldl

Haskell:foldl'累加器参数

在 Haskell 中即时减少列表

Haskell入门篇九:高阶函数(中)

[Haskell] 为什么列表操作++很昂贵?

Haskell 的 foldr/l 和 Clojure 的 reduce