What is the value of currying in functional programming?

I know the concept and how to use currying, but I wonder what its value is in practice?

+4
source share
3 answers

How is the practical use of curry functions related to this issue ? There are many reasons why people value currying and use it, including:

  • improved code reuse - special case functions - these are just partially applied (and curry) common functions
  • improved code readability - map (+2) easier to read than map (\x -> x + 2)
  • improved performance - currying can make an obvious certain specialization, and a good compiler will generate a specialized version for you.
  • fun - simpler code, more beautiful code makes life more enjoyable.
+4
source

The real benefits that I have found are:

  • Fewer mistakes - compiling code from a composition of functions results in more correct code than a forced control flow. For example, if you use a โ€œcardโ€ rather than โ€œfor loops,โ€ you eliminate the risk of many one-on-one errors.

  • Better is concurrency - and the code you create with clean, side-effect-free features is automatically thread safe. Combine this with immutable persistent data structures, and you have a great recipe for writing robust parallel code. Clojure is especially good for this - see http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey

  • More concise and manageable code - the functional code seems much shorter in my completely unscientific analysis than the imperative OOP code. The advantage tends to be more pronounced as the programs get larger. I think there are several reasons for this:

    • The syntax of functional languages โ€‹โ€‹tends to be fairly brief. For example, Haskell and various Lisps have very simple and elegant syntaxes.
    • Functional languages โ€‹โ€‹tend to encourage composition (higher-order functions) rather than "sticking together" parts of programs together. Therefore, you avoid the large number of patterns that are inherent in many other paradigms.
    • As for the composition point, I find it easier to apply the DRY principle in functional languages. If you see a generic pattern, you can almost always easily extract it into a higher-order function (or a macro if you are Lisper) so you can use it elsewhere.
  • Testability - when you write code mainly using pure functions, it is very easy to write reliable tests.

There are some disadvantages to doing this, of course:

  • It is harder to write - you need more mental flexibility because you need to keep quite complex abstract concepts in your head. This helps if you are a trained mathematician (s), but I still find it more difficult to write functional code than OOP.
  • There are some overhead costs . Functional languages โ€‹โ€‹use various constructs, which necessarily involve some degree of overhead. Although this can be done quite small with good compilers, it can never be completely eliminated.
  • Library / tool support is almost entirely due to the greater maturity of OOP platforms and tools, but it is still a problem. Ultimately this is not a problem, but the best solution I have found is to use Clojure, which can benefit most of the libraries and tools of the Java platform.
+2
source

I would say it is a bit like Once and Only Once

In C / C ++, I find myself writing code like

 configure_grid (grid, first_column, last_column, action) { for (i = first_column; i <= last_column; ++i) // ... } configure_grids (action) { congifure_grid (alpha, first_alpha, last_alpha, action); congifure_grid (beta, first_beta, last_beta, action); } 

Instead of writing for-loop once for each of alpha and beta. This is similar to considering procedural code. The advantage here is clear.

Currying is an important theoretical concept, but from a practical point of view it is an advantage.

In fact, I remember once writing a test suite in C, it was something like this:

 typedef bool (*predicate) (const type *); const char * argument; bool do_foo (const type * t) { return bar (t, argument); } bool do_baz (const type * t) { return bap (t, argument); } predicate foo (const char * arg) { argument = arg; return do_foo; } predicate baz (const char * arg) { argument = arg; return do_baz; } assert (for_all (data_set("alpha"), foo ("abc"))); assert (for_all (data_set("beta"), baz ("def"))); 

All this was in pure C, without macro definition, etc. Functional style and kind of like currying. The advantage here is that you can immediately see what the test cases are. data_set similar - it associates its argument with another function that retrieves data: for_all executes thunk, checks the predicate, and clears. Tidy.

+1
source

All Articles