Question

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
Was it helpful?

Solution

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'.

OTHER TIPS

That would be rather clever...

So clever that it would beat the hell out of the type system. What you want is array programming as in APL.

Is there any way I can (almost) do that?

I don't speak F#, but in Haskell, you'd uncurry F1, then compose with *10, then curry:

f2 = curry ((*10) . uncurry f1)

Which in an ML dialect such as F# becomes something like:

let curry f x y = f (x,y)
let uncurry f (x,y) = f x y

let mult x y = x * y

let F1 x y = x + y
let F2 = curry (uncurry F1 >> mult 10)

(I wasn't sure if curry and uncurry are in the F# standard library, so I defined them. There may also be a prettier way of doing partial application of * without defining mult.)

BTW, using point-free (or rather pointless in this case) approach one could define these functions in the following way:

let F1 = (+)
let F2 = (<<)((*)10) << F1
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top