Reputation: 7673
I am new to F# and I recently discovered the function composition operator >>
I understand the basic principle so that something like this is possible....
let Add1ToNum x = x +1
let Mul2ToNum y = y * 2
let FuncComp = Add1ToNum >> Mul2ToNum
However, how would one handle composition when you have several functions that have a varying number of input parameters... for instance I would like to be able to do the following...
let AddNums (x,y) = x+y
let MulNums (x,y) = x*y
let FuncComp = Add1 >> Mul2
Which obviously doesn't work because AddNums is returning an int, and MulNums is expecting a tuple.
Is there some form of syntax that allows me to accomplish this, or if I am wanting to use Function Composition do I have to always perform some sort of intermediary function to transform the values?
Anyone suggestions on this would be much appreciated.
Upvotes: 7
Views: 5661
Reputation: 3752
Yet another option is to make stack->stack functions much like an RPN calculator. For example:
let bin f = function a :: b :: t -> f b a :: t
let add = bin (+)
let mul = bin (*)
And perhaps a function to push literals to the stack:
let lit n t = n :: t
Then it's pure composition:
> (lit 9 >> lit 12 >> add >> lit 2 >> mul) []
42
You could even add stack shuffling functions:
let drop = function _ :: t -> t
let dup = function x :: t -> x :: x :: t
let swap = function x :: y :: t -> y :: x :: t
And do things like:
let square = dup >> mul
let cube = dup >> dup >> mul >> mul
let negate = lit -1 >> mul
Just an experiment in crazyness!
(See also http://blogs.msdn.com/b/ashleyf/archive/2011/04/21/programming-is-pointless.aspx)
Upvotes: 4
Reputation: 243126
As Yin and codekaizen pointed out, you cannot compose the two functions to create a function that passes the input to the first one and then passes the output of this call to the second function (that is, using th >>
operator). Using a diagram, you cannot do:
+---------+ +---------+
--->| AddNums |--->| MulNums |--->
+---------+ +---------+
One option is to change the function and specify one of the parameters, so that the functions can be composed. The example by codekaizen uses this and could be also written like this (if you used currying instead of tupled parameters):
let AddNums x y = x + y
let MulNums x y = x * y
let FuncComp = (AddNums 1) >> (MulNums 2)
Another option for composing the functions is to create a function that takes several inputs, passes two numbers to the first function and then calls the second function with the result and another number from the original inputs. Using a diagram:
-----------------\
--->+---------+ \+---------+
--->| AddNums |--->| MulNums |--->
+---------+ +---------+
If you need something like that, then the best option is to write that directly, because this probably won't be a frequently repeated pattern. Directly, this is easy (using the curried variant):
let AddNums x y = x + y
let MulNums x y = x * y
let FuncComp x y z = AddNums z y |> (MulNums z)
If you wanted to write something like that more generally (or just for curiosity), you could write something like this (using the tupled version of functions this time). The &&&
operator is inspired by Arrows:
let AddNums (x,y) = x + y
let MulNums (x,y) = x * y
let (&&&) f g (a, b) = (f a, g b)
let FuncComp = (AddNums &&& id) >> MulNums
// First two numbers are added, result is multiplied by the third one
FuncComp ((9, 12), 2) // Gives '42'
Upvotes: 11
Reputation: 27429
As Yin points out, your types don't match up when composing. AddNums
and MulNums
are of type int * int -> int
, so you can't expect to plug the output of one into the input of the other.
I do note that your last line is let FuncComp = Add1 >> Mul2
which might be a typo, but gives insight on how you can "bind" the functions which take tuples so that they compose:
let Add1 x = AddNums(x, 1)
let Mul2 x = MulNums(x, 2)
let FuncComp = Add1 >> Mul2
When run:
FuncComp(1);;
val it : int = 4
Upvotes: 2