haskell:使用不同类型的列表调用函数

时间:2017-05-09 13:28:32

标签: list haskell polymorphism

我有一个功能:

sum f l1 l2 = (f l1) + (f l2)

如何使用不同类型的列表调用此函数以使其正常工作?例如:

sum length [1,2] ['a','b']

2 个答案:

答案 0 :(得分:5)

也许在答案中充实我的评论。通常的签名可能是诱惑的是

sum :: Num b => ([a] -> b) -> [a] -> [a] -> b
sum f l1 l2 = f l1 + f l2

这里的问题是两个列表必须具有相同的类型,它必须是函数的输入类型。解决方案是告诉GHC该函数实际上具有更通用的类型forall a. [a] -> b,这意味着我们可以选择多个可能不同的 a实例,并且它们都生成相同的{ {1}}。

b

答案 1 :(得分:1)

不幸的是,目前还没有一般的方法可以做到这一点。您可以尝试按照以下方式previous answer建议:

sum' :: Num b => (forall a. [a] -> b) -> [c] -> [d] -> b
sum' f l1 l2 = f l1 + f l2

虽然这适用于length,但它并不适用于其他许多内容。

问题是此答案Num b => forall a. [a] -> b中的类型签名。这意味着您的函数必须适用于所有类型的列表,Num b => forall a. [a] -> b中唯一合理的函数是length。如果你认为另一个人可以随意给我一个例子,但我怀疑所有其他的例子都是长度的变化或愚蠢的例子返回一个常数。

如果lengthsum'唯一合理的论据,那么定义sum'很愚蠢,你可以定义sumLength,如下所示< / p>

sumLength :: Num b => [c] -> [d] -> b
sumLength l1 l2 = genericLength l1 + genericLength l2

确实,我们定义以下内容:

g :: Enum a => [a] -> Int
g = (foldl' 0 (+)) . (map fromEnum)

这是一个奇怪的可能无用的功能,但它做了一件非常重要的事情。它会将所有值转换为Enum int表示形式,然后将它们相加并吐出一个整数。

所以sum' g l1 l2应该有效,但事实并非如此。为了实现这一点,您必须定义一个新功能:

sum'' :: Enum c, Enum d => (Enum a => forall a. [a]) -> [c] -> [d] -> Int
sum'' f l1 l2 = f l1 + f l2

实际上,使用具有不同约束的任何函数也是如此,您必须定义新版本的sum

所以,真的,不,没有办法同样回答你的问题。

我发现了这个问题并创建了包polydata,你可以查看hackage(我需要一些更清晰的文档)。

它允许您创建接受可应用于不同类型的多态函数的函数,如下所示:

g :: (c (a -> a'), c (b -> b')) => Poly c -> (a, b) -> (a' -> b')
g f (x,y) = (getPoly f x, getPoly f y)

这与你的例子非常相似。

上面的

c是一个约束,查看g的类型可以帮助您了解发生了什么。

不幸的是,你不能将一个普通的函数传递给g,你必须传递一个包裹在Poly中的函数,这是非常重要的,因为你不能获得类型对Poly约束的推断(关于如何使这个更好的欣赏的任何想法)。

但如果你只有一个或几个需要这种多态行为的函数,我就不会为Poly而烦恼。但是,例如,你发现这个问题出现了很多(我发现它在单元测试中出现了很多,这是我的软件包创建的灵感),然后你可能会发现polydata很有用。

我还创建heterolist作为polydata的扩展,允许您创建混合类型的列表,并以类型安全的方式映射它们。你可能会发现它很有用。