我理解F#中函数组合的基础知识,例如描述here。
但是,也许我错过了一些东西。>>
和<<
运算符似乎是在假定每个函数只接受一个参数的情况下定义的:
> (>>);;
val it : (('a -> 'b) -> ('b -> 'c) -> 'a -> 'c) = <fun:it@214-13>
> (<<);;
val it : (('a -> 'b) -> ('c -> 'a) -> 'c -> 'b) = <fun:it@215-14>
然而,我想做的事情如下:
let add a b = a + b
let double c = 2*c
let addAndDouble = add >> double // bad!
但即使add
的输出属于double
输入所需的类型,也会被拒绝。
我知道我可以用一个元组参数重写add:
let add (a,b) = a + b
或者我可以为第一个函数的每个可能参数编写一个新运算符:
let inline (>>+) f g x y = g (f x y)
let doubleAdd = add >>+ double
但它看起来很傻!有没有更好的方式让我错过了?
答案 0 :(得分:12)
你想要的并不是完全不合理的,但是没有办法在F#的类型系统中指出广义合成运算符的类型。也就是说,没有好的方法来统一
(>>) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c
和
(>>+) : ('a -> 'b -> 'c) -> ('c -> 'd) -> 'a -> 'b -> 'd
(更不用说无数更高级的arity版本)。因此,除了定义自己的附加运算符之外别无选择。在实践中,我经常发现以“尖头”样式let f x y = add x y |> double
编写的代码比无点/“无意义”let f = add (>>+) double
更具可读性。
答案 1 :(得分:4)
查看您在上面发布的>>
和<<
的类型。例如:
> (>>);;
val it : (('a -> 'b) -> ('b -> 'c) -> 'a -> 'c) = <fun:it@214-13>
它需要两个函数和一个值('a
)并返回另一个值。你需要一些需要两个函数和两个值的东西。因此,>>
和<<
都没有正确的类型签名。
你的实施根本不是愚蠢的。只是你的要求并没有在F#的库中开箱即用。要感谢你有一种语言允许你定义自己的运算符,如下所示:)
答案 2 :(得分:3)
如何通过线程化一堆参数?
let add = function x :: y :: t -> x + y :: t
let double = function x :: t -> 2 * x :: t
然后你可以组成任意的arity函数:
let doubleAdd = add >> double
它有效:
> doubleAdd [7; 14]
42