我试图通过Haskell来理解函数式编程,而且我在处理函数组合方面遇到了很多麻烦。
实际上我有这两个功能:
add:: Integer -> Integer -> Integer
add x y = x + y
sub:: Integer -> Integer -> Integer
sub x y = x - y
我希望能够撰写它们。它没有任何意义,但它是一个学习目标。
我尝试过的事情:
foo:: (Integer -> Integer) -> (Integer -> Integer) -> Integer
foo = add . sub
我明白了:
Haskell使用只有一个args的函数,因此我们返回一个新函数,在每次执行函数后执行。
所以第一个Integer
是param类型,而第二个是生成函数的返回类型,必须添加第二个数字。
这将返回另一个函数(sub
),该函数将生成相同的流程(返回带有参数等的函数...)
我是对的吗?
以下是我的实际错误代码:
src\Main.hs:23:7:
Couldn't match type `Integer' with `Integer -> Integer'
Expected type: Integer -> (Integer -> Integer) -> Integer
Actual type: Integer -> Integer -> Integer
In the first argument of `(.)', namely `add'
In the expression: add . sub
src\Main.hs:23:13:
Couldn't match type `Integer -> Integer' with `Integer'
Expected type: (Integer -> Integer) -> Integer
Actual type: Integer -> Integer -> Integer
Probable cause: `sub' is applied to too few arguments
In the second argument of `(.)', namely `sub'
In the expression: add . sub
我不知道自己做错了什么。
你能帮我理解一下这个错误,以便找到解决方案吗?
答案 0 :(得分:7)
给定功能
add :: Integer -> Integer -> Integer
记住(正如您在我理解的内容部分中指出的那样),类型签名中的->
与右侧相关联,即上述类型是有用的与
add :: Integer -> (Integer -> Integer)
现在,考虑(.)
:
(.) :: (b -> c) -> (a -> b) -> a -> c
这意味着在表达式中
(.) add
b
类型中的(.)
为Integer
,c
对应Integer -> Integer
。写这个的另一种方法是
b ~ Integer
c ~ Integer -> Integer
所以我们得到
(.) add :: (a -> Integer) -> a -> (Integer -> Integer)
如果您现在将(.) add
应用于sub
,则编译器会注意到a -> Integer
无法与Integer -> Integer -> Integer
匹配。
我怀疑你可能希望组合使用三个参数:两个应用sub
,然后结果与第三个参数一起传递给{ {1}}。因此,组成这两个函数的可能定义是
add
对于它的价值,存在一个相关的问题:用一个参数函数组成一个两个参数函数,例如:构成
答案 1 :(得分:5)
我希望能够撰写它们。它没有任何意义,但这是为了学习目标。
这实际上是问题所在。你想怎么写他们?让我们看看一些可能的成分:
foo x y = sub x (add x y) -- x + y - x = y
foo x y = sub y (add x y) -- x + y - y = x
foo x y = sub x (add y y) -- 2 * y - x
foo x y = sub y (add y y) -- 2 * y - y = y
foo x y = sub y (sub y (add x x)) -- 2 * x - 2 * y
话虽如此,让我们通过检查手中的类型来检查类型错误:
type I = Integer -- otherwise the lines are going to be very long
(.) :: (b -> c ) -> (a -> b ) -> a -> c
add :: I -> (I -> I)
sub :: I -> (I -> I)
-- |||||||||||||
(.) add :: (a -> I ) -> a -> (I -> I)
-- ^^^^^^^^^^^^^
如您所见,(.) add
已经强制要求其他函数只能为任意a -> Integer
设置a
类型。但sub
的类型为Integer -> (Integer -> Integer)
(请记住,(->)
是右关联的。)
现在,你能做些什么才能真正解决这个问题?首先,让我们检查您提议的foo
类型:
foo :: (Integer -> Integer) -> (Integer -> Integer) -> Integer
这实际上是非常有趣的功能类型。你怎么会得到你的结果?你手头有两个函数,但没有值:
> foo f g =
您可以使用其中一个函数的固定点解决此问题,然后应用另一个函数:
> let x = f x in g x
>
> example = foo (const 12) (+1) -- returns 13
但那不是你的意思,对吧?在这一点上,考虑你的作品的语义是非常重要的。由于这些不清楚,你不能写一般的方法来组合这两个功能。
但是,如果实际上意味着
foo :: Integer -> Integer -> Integer -> Integer
foo x y z = add (sub x y) z
那么
就可以了foo = (add .) . sub
因为
(.) add :: (a -> I) -> a -> (I -> I)
(.) ((.) add) :: (a -> b -> Integer) -> a -> b -> Integer -> Integer
但(add .) . sub
不再那么容易了。如果这种函数是你最初的目标,你最好写一个foo
的逐点定义。