Haskell 类型签名

3

我是Haskell的新手,看到了一些关于类型签名的东西,但有几个问题我不太明白。

这是我正在查看的代码:

--mult applies product
mult :: Num a => [a] -> a
mult  = foldr (*) 1 

--posList filters positive numbers out
posList :: (Ord a, Num a) => [a] -> [a]
posList = filter (>0) 

--trueList determines whether all of the members of a list are T or not
trueList :: [Bool] -> Bool
trueList  =  foldr(&&) True 

--evenList determines where all of the members of a list are even or not
evenList :: (Integral a, Foldable t) => t a -> Bool
evenList x = if (((foldr (+) 2 x ) `mod` 2) == 0) then True else False

我知道你可以用不同的方法更容易地完成这些功能,但是我必须使用高阶函数mapfilterfoldr来完成我的任务。无论如何,由于是我自己编写了这些函数,所以我理解它们的工作原理,但是我不理解那些术语IntegralFoldable的含义是什么?在Haskell中它们又被称为什么?


3
你测试过 evenList 了吗?我建议你尝试一下 evenList [1,3] - chi
1
Integral 和 Foldable 是 类型类,请搜索此术语。基本上 Foldable t 的意思是 "t 是一个类似于列表的类型"。 - n. m.
类型类是 Haskell 中实现重载的方式。一个类型通过实现一些函数来实现一个类型类。例如,(+) 的类型是 Num a => a -> a -> a,因此如果我定义了一个类型 Foo,我想要能够进行加法运算,那么我需要定义 (+) :: Foo -> Foo -> Foo,这只能通过使用 Foo 实例化 Num 类来完成。 - Dan Robertson
@chi 哈哈哈,谢谢你提醒我,我已经修复了。evenList :: [Int] -> Bool evenList xs = foldr (&&) (True) (map (even) xs) - Mostafa Ibrahim
@MostafaIbrahim 好多了;-) - chi
1个回答

5

这些是对多态类型的限制。

Haskell虽然是静态类型语言,但通过一种称为parametric polymorphism的系统,非常容易编写可以处理不同类型的函数。首先,我会给出所有函数的具体(单态)签名:

mult :: [Integer] -> Integer
mult  = foldl' (*) 1  -- foldl' is better for performance/memory than foldr

posList :: [Integer] -> [Integer]
posList = filter (>0) 

trueList :: [Bool] -> Bool
trueList  =  foldl' (&&) True

evenList :: [Integer] -> Bool
evenList x = foldl' (+) 2 x `mod` 2 == 0
            -- `if True then True else False` is tautological

所有这些签名都有效(与改进的实现和您的原始签名都有效)。
但是它们仅适用于Integer列表。这并不总是足够通用;例如,计算分数数列的乘积是完全合理的。但是使用单态签名,mult [1.5, 2.4, 20]不起作用:这些数字与Integer不兼容。您不想限制函数只能使用整数,而是希望它可以使用任何数字类型,并始终获得与元素相同类型的结果。也就是说,您基本上需要这个签名。
mult :: ∀ a . [a] -> a

……被阅读为“对于所有类型a,函数mult接受一个元素类型为a的列表,并给出单个a值作为结果。在Haskell中,当您拥有这样的类型变量时,是隐含的,换句话说,您也可以将其表示为

mult :: [a] -> a

然而,这还不起作用,因为该函数必须能够乘以元素。但是,并非对于所有类型都可以实现,仅适用于数字类型。因此,您需要添加约束条件。

mult :: Num a => [a] -> a

对于posList,情况基本相同:签名本质上是
posList :: [a] -> [a]

但你也需要能够使用Ord比较元素和0 (Num)。因此有一个限制条件。

posList :: (Num a, Ord a) => [a] -> [a]

对于evenList,数值操作包括(+), (==)mod。因此,原则上我们需要NumEqIntegral,但是Integral已经作为超类包含了NumEq,所以这就足够了:

evenList :: Integral a => [a] -> Bool

......这还不是最通用的形式。您可以使用折叠函数来减少该列表,但列表并不是唯一可以折叠的内容,例如您也可以折叠数组、映射和Maybe值。所有可以被折叠的容器的类型类被称为Foldable。因此我们得到

evenList :: (Integral a, Foldable t) => t a -> Bool

你可以将同样的概括应用到 `mult` 和 `trueList`:
mult :: (Num a, Foldable t) => t a -> a
trueList :: Foldable t => t Bool -> Bool

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接