I'd like to compose functions in a certain way. Please consider these 2 functions in pseudocode (not F#)
F1 = x + y
F2 = F1 * 10 // note I did not specify arguments for F1, 'reverse curry' for lack of a better word
What I would like for F# to do is figure out that since
let F1 x y = x + y
//val F1 : int -> int -> int
the code let F2 = F1 * 10
would give me the same signature as F1: val F2 : int -> int -> int
, and calling F2 2 3
would result in 50: (2 + 3) * 10. That would be rather clever...
What happens is quite different tho. The first line goes as expected:
let F1 x y = x + y
//val F1 : int -> int -> int
but when I add a second line let F2 = F1 * 10
it throws off F#. It complains that the type int does not match the type 'a -> 'b -> 'c
and that F1 now requires member ( + )
.
I could of course spell it out like this:
let F1(x, y) = x + y
let F2(x, y) = F1(x, y) * 10
But now I might as well have used C#, we're not that far away anymore. The tupled arguments break a lot of the elegance of F#. Also my real functions F1 and F2 have a lot more arguments than just 2, so this makes me go cross eyed, exactly what I wanted to dodge by using F#. Saying it like this would be much more natural:
let F1 x y = x + y
let F2 = F1 * 10
Is there any way I can (almost) do that?
For extra credits: what exactly goes on with these error messages? Why does the second line let F2 = F1 * 10
change the typing on the first?
Thanks in advance for your thoughts,
Gert-Jan
update Two apporaches that (almost) do what's described.
One using a tuple. Second line looks a little quirky a first, works fine. Small drawback is I can't use currying now or I'll have to add even more quirky code.
let F1 (a, b) = a + b
let F2 = F1 >> (*) 10
F2(2, 3) // returns 50
Another approach is using a record. That is a little more straight forward and easier to get at first glance, but requieres more code and ceremony. Does remove some of the elegance of F#, looks more like C#.
type Arg (a, b) =
member this.A = a
member this.B = b
let F1 (a:Arg) = a.A + a.B
let F2 (a:Arg) = F1(a) * 10
F2 (Arg(2, 3)) // returns 50
There is no pattern for this in general. Using combinators (like curry
and uncurry
) as suggested by larsmans is one option, but I think the result is less readable and longer than the explicit version.
If you use this particular pattern often, you could define an operator for multiplying a function (with two parameters) by a scalar:
let ( ** ) f x = fun a b -> (f a b) * x
let F1 x y = x + y
let F2 = F1 ** 10
Unfortunately, you cannot add implementation of standard numeric operators (*
, etc.) to existing types (such as 'a -> 'b -> int
). However, this is quite frequent request (and it would be useful for other things). Alternatively, you could wrap the function into some object that provides overloaded numeric operators (and contains some Invoke
method for running the function).
I think an appropriate name for this would be lifting - you're lifting the *
operator (working on integers) to a version that works on functions returning integers. It is similar to lifting that is done in the C# compiler when you use *
to work with nullable types.
To explain the error message - It complains about the expression F1 * 10
:
error FS0001: The type 'int' does not match the type ''a -> 'b -> 'c'
I think it means that the compiler is trying to find an instantiation for the *
operator. From the right-hand side, it figures out that this should be int
, so it thinks that the left-hand side should also be int
- but it is actually a function of two arguments - something like 'a -> 'b -> c'
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With