20

可能重复:
为什么在haskell中不允许这样的函数定义?

我是从 Lisp 迁移过来的 Haskell 世界的新手。我正在努力适应 Haskell 完全不同的世界观,而我发现许多新的和令人兴奋的事情之一就是类型系统。作为一个 Lisper,我想我会尝试在 Haskell 中实现一个在 Lisp 世界中非常重要的功能:apply. 对于那些不知道的人,apply 接受一个函数和一个参数列表,并在这些参数上调用函数。在 Scheme 中,(apply + '(1 2 3))与调用 相同(+ 1 2 3),返回 6。

我的 Haskell 代码如下所示:

apply x [] = x
apply f (x:xs) = apply (f x) xs

但是 Haskell 抱怨说:

ERROR line 2 - Type error in function binding
*** Term           : apply
*** Type           : (b -> a) -> [b] -> a
*** Does not match : a -> [b] -> a
*** Because        : unification would give infinite type

我想我明白为什么。Apply 的类型需要根据给出的列表的长度而有所不同。给定一个包含 3 个项目的列表,apply 的类型需要是: (a -> a -> a -> b) -> [a] -> b,但是给定一个包含 6 个项目的列表,apply 的类型需要是: (a -> a -> a -> a -> a -> a -> b) -> [a] -> b

我尝试了这个可怕的解决方法:

data FnOrDat a b = Dat b | Fn (a -> FnOrDat a b)

apply :: (FnOrDat a b) -> [a] -> (FnOrDat a b)
apply x [] = x
apply (Fn f) (x:xs) = apply (f x) xs
apply (Dat _) _ = error "Cannot apply something which is not a function!"

add a = Fn (\b -> Dat (a + b))

main = putStrLn $ show $ x where Dat x = apply (Fn add) [5,1]

这行得通,但它几乎不能算作一个apply函数,因为我不能传递apply一个普通的函数,我必须使用一个专门编写的函数来使用我的(尴尬的)FnOrDat 抽象。如果我想编写一个添加四个数字的函数,我需要编写

add4 a = Fn (\b -> Fn (\c -> Fn (\d -> Dat (a + b + c + d))))

嗯。

那么 - 我是否遗漏了什么,或者要求一个通用的,apply基本上就像要求一个可以操作任意长度元组的函数?在 Haskell 的静态类型世界观中是否apply有意义?

4

3 回答 3

11

apply在 Haskell 中不是很有用,因为你不能给函数一个类型。正如您在 FnOrDat 中看到的那样,您实际上是将 Lisp 语言作为 EDSL 嵌入到 Haskell 中以强制通过。

要求通用应用基本上就像要求可以操作任意长度元组的函数?

确切地。您可以为某些有用的类型组合提供类型类实例,但实际上并不需要或使用通用 variadic apply


作为旁注,您应该考虑升级到 GHC 和Haskell 平台,而不是过时的 Hugs 系统,因为您错过了过去 10 年开发的大多数库、工具和语言功能。

于 2012-05-26T15:21:01.980 回答
4

尽管有 Don 的解释,foldl1 (+)但实际上会添加列表的所有元素。因此,可以说fold函数族非常接近applyOP 所描述的函数。

于 2012-05-26T15:36:48.567 回答
1

... 在 Lisp 世界中非常重要的一个函数:apply。对于那些不知道的人,apply 接受一个函数和一个参数列表,并在这些参数上调用函数。在 Scheme 中,(apply + '(1 2 3)) 与调用(+ 1 2 3)相同,并返回6。...

这很简单:

foldr  (+) 0 [1,2,3]
foldr1 (+)   [1,2,3]

结果为 6。

要将函数应用于列表的每个元素:

map f list

例如

map (2*) [1,2,3]

结果 [2,4,6]

这是你想要的?

于 2012-05-28T14:39:01.677 回答