Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When should I write my functions in curried form?

I've recently started learning F# and come across curried functions for simple examples such as the following:

Consider a function that calculates sales by multiplying price p by number of units sold n.

let sales (p,n) = p * (float n);;

The type of this function is given as

val sales : p:float * n:int -> float

i.e. take a pair of float and int and returns a float.

We can instead write this as a curried function

let salesHi p n = p * (float n);;

The type of this function is given as

val salesHi : p:float -> n:int -> float

i.e. takes a float and returns a function of int to float.

In simple cases this seems to make no difference

sales (0.99, 100);;
salesHi 0.99 100;;

Both give

val it : float = 99.0

However with the curried function I can feed in the price for particular items to get new functions. E.g.

let salesBeer =  salesHi 5.99;;
let salesWine =  salesHi 14.99;;

Then salesBeer 2 gives 11.98 and salesWine 2 gives 29.98.

Also, I've noticed that built-in operators such as + are defined as functions, so I can write, for example:

let plus2 = (+) 2;
List.map plus2 [1;3;-1];;

and get

val it : int list = [3; 5; 1]

This seems like a good thing. So when I want to implement a function in an imperative language that would have taken n > 1 arguments, should I for example always use a curried function in F# (so long as the arguments are independent)? Or should I take the simple route and use regular function with an n-tuple and curry later on if necessary? Or something else?

How do F# programmers decide when to make a function in curried form or use a regular function with a tuple?

like image 632
TooTone Avatar asked Sep 10 '13 11:09

TooTone


People also ask

When would you use a currying function?

Currying is helpful when you have to frequently call a function with a fixed argument. Considering, for example, the following function: If we want to define the function error , warn , and info , for every type, we have two options. Currying provides a shorter, concise, and more readable solution.

Why should we use currying in JavaScript?

There are several reasons why currying is ideal: Currying is a checking method to make sure that you get everything you need before you proceed. It helps you to avoid passing the same variable again and again. It divides your function into multiple smaller functions that can handle one responsibility.

Is a curried function a higher order function?

Higher-order functions are functions that take functions as parameters or return functions when called. Curried functions are higher-order functions that take one argument at a time returning a series of functions until all parameters are passed.

Can you give an example of a curry function and why this syntax offers an advantage?

Curry functions are neat when used to carry containers of reusable code. Basically you take a function with multiple arguments and you know that one of those arguments will have specific value but the other is undecided.


Video Answer


3 Answers

When you're choosing between curried and tupled form, the main thing to consider is whether the tuple that you'd take as an argument means anything.

Tupled form. For example, float * float might represent a range and then it is a good idea to use the tupled form.

let normalizeRange (lo, hi) = if hi < lo then (hi, lo) else (lo, hi)
let expandRange by (lo, hi) = (lo - by, hi + by)

The good thing about this is that you can then compose functions that work on ranges. You can for example write something like:

randomRange() |> normalizeRange |> expandRange 10

Curried form. On the other hand, the curried form is a better choice if the tuple of all arguments is not a stand-alone value with some useful meaning. For example, the power function pown 2.0 10 - the two arguments are the number to power and the exponent, but it is unlikely that you'd ever use the tuple (2.0, 10) somewhere else in your program.

The curried form is also useful when you have one "more important" argument, because then you can use pipelining. For example, List.map has to be curried to allow this:

[1 .. 10] |> List.map (fun n -> n + 1)
like image 128
Tomas Petricek Avatar answered Oct 18 '22 02:10

Tomas Petricek


Or should I take the simple route and use regular function with an n-tuple and curry later on if necessary?

What do you find simpler about doing more work? let stuff x y z = ... is not just less typing than let stuff (x, y, z) it is actually less work done by the language. The second version has to allocate a tuple then destructure the tuple into arguments vs the first version which just consumes the arguments.

The curried form is the idiomatic way to write functions in F#. I really can't think of a good reason to use the tuple form unless the data was already stored as a tuple.

like image 43
stonemetal Avatar answered Oct 18 '22 03:10

stonemetal


One other consideration - if you're planning on interoperating with C# or VB .NET, don't bother with the curried form as they aren't exposed that nicely to those languages. On the other hand the tupled form is exposed as a normal set of arguments from a C# / VB .NET point of view and is very natural to consume.

like image 3
Isaac Abraham Avatar answered Oct 18 '22 02:10

Isaac Abraham