例如,我做不到
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中没有实现这么简单的事情?
答案 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)
类型的应用函子在这些情况下更容易表达自己。一旦我有更多时间,我会尝试添加它。