Haskell是否支持部分应用函数的组合?

时间:2017-09-02 07:27:32

标签: haskell

例如,我做不到

f = ((*2).(+)) 3

这是好的,因为我总能做到

f = (*2).(+3)

但是当我想创建一个功能列表时,这有点不方便,例如

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

map (+) [1, 2, 3]

没关系。

当然,我总是可以使用lambda表示法来实现currying:

map (\x y -> 2*(x + y)) [1, 2, 3]

据我所知,GHC不喜欢编写部分应用的函数,因为它不知道如何将函数类型提供给像(* 2)这样的操作。

(+) 2 :: Num a => a -> a 
(*2) :: Num a => a -> a 

但我一直认为它应该是一个相当自然的东西:(+)的输出类型是Num a => a,所以(* 2)应该能够'吃'那个。

我的问题是:这是以某种方式实施的吗?或者,是否有人知道为什么在Haskell中没有实现这么简单的事情?

2 个答案:

答案 0 :(得分:6)

Haskell支持部分函数的组合但你有类型不匹配。构成是功能

(.) :: (b -> c) -> (a -> b) -> a -> c

在你的情况下你有两个功能

(*2) :: Num a => a -> a
(+)  :: Num a => a -> a -> a

当你编写这个函数时,结果类型将是

((*2).(+)) :: (Num (a -> a), Num a) => a -> a -> a

那不是你想要的。您可以重写函数f,例如(.) (*2) . (+),但我认为lambda更具可读性。

你混淆了部分功能和部分应用。部分函数是不为整个域定义的函数,部分应用程序是将多个参数固定到函数的过程,产生较小的arity函数。

答案 1 :(得分:1)

如果要点免费,可能会让人感到困惑。如果我们通过类型签名;

(.)(b -> c) -> (a -> b) -> a -> c

(+)Num a => a -> a -> a

(2*)Num a => a -> a

现在,如果我们将(2*) . (+)作为(.) (+)的第二个参数,则必须有一个类型(a -> b),当我们重写{{}时,它实际上是真的1}}类型为(+)。所以我们的Num a => a -> (a -> a)类型恰好是b。现在请记住Num a => a -> a(.)作为第一个参数。我们在这里遇到一些问题,因为我们的表达式(b -> c) (2*) . (+)(2*)类似b类似Num a => a类似b类型Num a => a -> a }。你不能将函数乘以2 ..!我们必须将(2*)转换为这样一个函数,该函数将采用Num a => a -> a类型的函数,运行它并将其结果提供给(2*)。不需要看这不超过(.)。在这种情况下,如果我们再次像((2*) . )一样写,我们可以安全地提供我们的Num a => a -> a类型函数(请记住这部分应用(+))。让我们把它包起来......

我们的免费等效\x y -> 2*(x + y)((2*) . ) . (+)

Prelude> (((2*) . ) . (+)) 3 5
16

我们可能会使用((->) r)类型的应用函子在这些情况下更容易表达自己。一旦我有更多时间,我会尝试添加它。