Haskell组成有两个参数

时间:2016-01-12 07:35:53

标签: haskell functional-programming function-composition

我试图通过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

我不知道自己做错了什么。

你能帮我理解一下这个错误,以便找到解决方案吗?

2 个答案:

答案 0 :(得分:7)

给定功能

add :: Integer -> Integer -> Integer

记住(正如您在我理解的内容部分中指出的那样),类型签名中的->与右侧相关联,即上述类型是有用的与

相同
add :: Integer -> (Integer -> Integer)

现在,考虑(.)

的类型
(.) :: (b -> c) -> (a -> b) -> a -> c

这意味着在表达式中

(.) add

b类型中的(.)Integerc对应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的逐点定义。