Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What am I missing: is function composition with multiple arguments possible?

I understand the basics of function composition in F#, as, for example, described here.

Maybe I am missing something, though. The >> and << operators seem to have been defined with the assumption that each function only takes one argument:

> (>>);;
val it : (('a -> 'b) -> ('b -> 'c) -> 'a -> 'c) = <fun:it@214-13>
> (<<);;
val it : (('a -> 'b) -> ('c -> 'a) -> 'c -> 'b) = <fun:it@215-14>

What I'd like to do, however, is something like the following:

let add a b = a + b
let double c = 2*c
let addAndDouble = add >> double   // bad!

But even though add's output is of the type required for double's input, that is rejected.

I know that I can rewrite add with one tuple argument:

let add (a,b) = a + b

Or I can write a new operator for every number of possible arguments to the first function:

let inline (>>+) f g x y = g (f x y)
let doubleAdd = add >>+ double

But it seems silly! Is there a better way that I've missed?

like image 726
Kevin Cantu Avatar asked Mar 26 '11 23:03

Kevin Cantu


3 Answers

How about threading a stack of arguments through?

let add = function x :: y :: t -> x + y :: t
let double = function x :: t -> 2 * x :: t

Then you can compose arbitrary arity functions:

let doubleAdd = add >> double

And it works:

> doubleAdd [7; 14]
42

(see also F# Function Composition with multiple input parameters)

like image 66
AshleyF Avatar answered Nov 13 '22 22:11

AshleyF


What you want isn't totally unreasonable, but there would be no way to indicate the type of a generalized composition operator within F#'s type system. That is, there's no good way to unify

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

and

(>>+) : ('a -> 'b -> 'c) -> ('c -> 'd) -> 'a -> 'b -> 'd

(not to mention the infinitely many higher arity versions). Therefore, you have no alternative but to define your own additional operators. In practice, I often find code written in the "pointed" style let f x y = add x y |> double more readable than the point-free/"pointless" let f = add (>>+) double anyway.

like image 41
kvb Avatar answered Nov 13 '22 23:11

kvb


Look at the types of >> and << that you posted above. eg:

> (>>);;
val it : (('a -> 'b) -> ('b -> 'c) -> 'a -> 'c) = <fun:it@214-13>

It takes two functions and a value ('a) and returns another value. You need something that takes two functions and 2 values. Hence, both >> and << don't have the correct type signature.

Your implementation isn't silly at all. It's just that your requirement doesn't come out of the box in F#'s libraries. Be thankful that you have a language which allows you to define your own operators like this :)

like image 4
OJ. Avatar answered Nov 13 '22 21:11

OJ.