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

时间:2018-02-28 18:41:50

标签: haskell types syntax signature

当我询问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就没有多大意义了。什么是"空的可折叠"我可以用作基本情况吗?

4 个答案:

答案 0 :(得分:6)

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])

答案 1 :(得分:6)

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

所以: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.Set的内容,并且不能与其他Foldable实例一起使用。

答案 2 :(得分:3)

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

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

     

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

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

继续讨论您的红利问题:foldl恰好是Foldable课程的一种方法。这意味着,在为类型编写Foldable实例时,您可以提供foldl的适当定义,您可以在其中使用该类型的特定功能(例如[] 1}}是列表的构造函数 - 另请参阅Daniel Wagner's answer)。

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

答案 3 :(得分:2)

在上下文(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甚至不是一种类型,您无法在=>的右侧使用它。