pascal
pascal

Reputation: 21

How to directly reuse a function returned from another one in F#

I'm currently working on a project where I have to create a lot of functions according to some rules and reuse those functions later. And for that I started to learn F#, just seemed to be a more natural thing to do in F# than in C#.

I managed to create the functions as they should be but I have some problems whith reusing them. Below is a very simplified example, to show what I'm trying to do.

let choice = 2

let fCreator (x:float) =
    match choice with
    |1 ->  x * x
    |2 ->  x * 10.0
    |3 ->  x + 20.0
    |_ ->  x

I have a global variable choice according to which in fCreator a transformation of x is selected and returned. This works fine by calling it e.g.

let result1 = fCreator 1.0

But the problem is that I have to use the selected transformation (in that case x*10) for a lot different inputs and when using it this way

let result2 = fCreator 2.0
let result3 = fCreator 3.0
.
.
.

then the whole selection process is done again, which is no big deal here but my actual selection function is a far more complicated one and takes some time to compute.

Is it possible to call fCreator, get and save the selected transformation and then directly use it? Something like

let selectedFunction x:float = fCreator x:float

where then

selectedFunction x:float = x * 10

is

I know the above wont work that way, but I hope you get the idea what I'm trying to do. selectedFunction then should be the chosen transformation from fCreator.

I'm quite new to F# so I might be overlooking something obvious but I perused several Q&As according to function caching in F#. But as far as I can see neither memoizing nor lazy evaluation will do what I'm looking for.

I also tried to write fCreator as a void function with x in the body as a declared but not initialized variable and then returning a transformation with that variable. But it didn't quite work out and is as much as I know also not the proper way to do in F#.

I hope that someone could give me a hint here.

Upvotes: 2

Views: 212

Answers (4)

pascal
pascal

Reputation: 21

Thank you all very much for your responses!

So the overall idea is to use anonymous functions as return values in my selection function. This works perfectly fine and is a simple way to do in the above examples.

But the problem is (at least for me) that in my actual selection function the transformations are created through lots of recursive function calls, which makes it not that easy to define them as anonymous functions. The created transformations (like e.g. x*x above) then are actually sequences of operations and recursive functions and best thing would have been to let them unchanged.

I hoped it might be possible to store the selected transformation only by changing the code that calls the selection function. So that I could get something like the creator function in Mark's example, but keeping the return values in selectCreator as they are in my original example (not as anonymous functions).

Upvotes: 0

Søren Debois
Søren Debois

Reputation: 5688

You have the right idea, reusing a function "returned from another one". You can do it like this:

let mkCreator choice = function
    | 1 -> fun x -> x * x
    | 2 -> ( *) 10.0
    | 3 -> (+) 20.0
    | _ -> id

let fCreator = mkCreator 2
fCreator 20.0               (* val it = 200.0 : float *)

This makes the "expensive" mkCreator call happen only once.


Another answer suggested the following:

let mkCreator choice x = function
    | 1 -> x * x
    | 2 -> (*) 10.0
    | 3 -> (+) 20.0
    | _ -> x

let fCreator = mkCreator 2

However, the body of mkCreator won't be evaluated until it has all of it's arguments. That means that every time you subsequently apply fCreator, you will evaluate the body of mkCreator. But this was exactly what we were trying to avoid!

Upvotes: 1

Mark Seemann
Mark Seemann

Reputation: 233150

Instead of relying on a global value, define a function that returns another function:

let selectCreator choice =
    match choice with
    | 1 -> fun x -> x * x
    | 2 -> fun x -> x * 10.0
    | 3 -> fun x -> x + 20.0
    | _ -> id

This is a function that returns the desired function. It has the signature int -> (float -> float). You can bind it to a particular choice like this:

let creator = selectCreator 2

You can now use creator with various values:

> creator 2.0;;
val it : float = 20.0
> creator 3.0;;
val it : float = 30.0

The selection only happens when you invoke selectCreator; every time you call creator, you simply call the returned function. This can be illustrated by making the following modification to selectCreator:

let selectCreator choice =
    match choice with
    | 1 ->
        printf "1 selected"
        fun x -> x * x
    | 2 ->
        printf "2 selected"
        fun x -> x * 10.0
    | 3 ->
        printf "3 selected"
        fun x -> x + 20.0
    | _ ->
        printf "Default selected"
        id

Although in Functional Programming, we dislike side-effects, it's a great way to demonstrate that this works as intended. Here's a log of an FSI session:

> let creator = selectCreator 2;;
2 selected
val creator : (float -> float)

> creator 4.0;;
val it : float = 40.0

> creator 5.0;;
val it : float = 50.0

Notice that it only prints out the selection when creator is selected, not when it's invoked.

This approach also has the advantage that you change your mind at run-time, and select a different function:

> let creator3 = selectCreator 3;;
3 selected
val creator3 : (float -> float)

> creator3 4.0;;
val it : float = 24.0
> creator3 5.0;;
val it : float = 25.0

Upvotes: 2

Tarmil
Tarmil

Reputation: 11362

If the choice is computed once and then always the same, then you can do something like this:

let choice = 2

let fCreator =
    match choice with
    | 1 -> fun x -> x * x
    | 2 -> fun x -> x * 10
    | 3 -> fun x -> x + 20
    | _ -> x

This will return a whole different function depending on the value of choice.

Upvotes: 1

Related Questions