Hacker News new | ask | show | jobs
by simula67 3878 days ago
I am having a lot of trouble trying to understand the hype behind functional programming. I have read McCarthy's paper on LISP, completed Odersky's course on Scala etc. No revelation so far ( yes, maybe I am stupid, but I won't admit it ). Is it only useful for study as a model that inspired modern programming languages ? For example :

"obvious power of code becoming data."

Many languages have eval() where data can be treated as code.

13 comments

Traverse a <String, Int> hashmap, remove all strings with an even key string size and sort it by the last character in the string, then add the string size to the integer (here you have a list of integers) and sum the product of the numbers at index 0 and n-1, 1 and n-2, ...

Compare the the code size in Java and Clojure.

If that is not enough, do the same thing but start with java objects (and their clojure equivalent: a hashmap) for some given object/map property.

EDIT: And if that is not enough, use as property of the object a string, defined only during run-time by a user input.

Exactly this. The amount of code to do things is shockingly small.

When I was first learning Clojure I stumbled on a "lack of good documentation" in third party libraries. I'd google around for something that solved the problem I was trying to solve so I wouldn't reinvent the wheel. I'd find, say, a github repo that had a couple of sentences in its README that purported to solve my problem and then nothing else. What the hell, I thought; why no decent doc? Then I realized: the solution was implemented in 40 lines of Clojure. Reading the source was the fastest way to figure it out.

It still happens to me. Every time it does, I have this warm feeling of investing in something of great value.

I also use several libraries that don't have a decent (or any) doc, just docstrings in source code and I kind of got used to it now, or, as Emacs puts it:

Code never lies, comments sometimes do (Charles de Gaulle)

I'd love to see some example code for doing this.

I'm trying to implement it in Kotlin and am curious how it would compare.

  (def mymap {"asd" 1 "qwe" 2 "rtz" 3 "foo" 4 "bar" 5 "quz" 6 "bnm" 7})

  (let [ints (->> (filter (comp odd? count key)  mymap)
                                   (sort-by (comp int last first))
                                   (map (fn [[s i]]
                                          (+ (count s) i))))]
                     (reduce + 0 (map * ints (reverse ints))))
Gives me 341
As other commenters said, you're confusing hype behind Lisp with hype behind FP. They may be related, but are not the same thing.

Almost all currently used languages have some functional programming support. Many "modern" languages are functional in nature, even if it's well hidden behind syntax sugar.

Lisps, and code-as-data, is different, in that it's about compile-time meta-programming and not runtime execution model. There are non-lisp languages which offer similar capabilities, see Elixir and Nimrod for some recent examples.

Anyway, if you want to understand practical advantages of FP you should just use it in practice. It shouldn't really come as a surprise that you can't see practical benefits if you only studied a bit of theory...

I think you're confusing two concepts:

- functional programming (Haskell, Scheme, Clojure) which favors recursion and stateless functions

- homoiconicity (code=data) (all Lisps)

These two concepts are completely orthogonal, and there are many Lisps such as Common Lisp, Emacs Lisp where functional programming style is not encouraged. Most of the code in these languages can hardly be called functional.

Conversely, not all functional programming languages are Lisps or homoiconic, in fact most of them aren't.

> Many languages have eval() where data can be treated as code.

I will just address this point, which IMHO is orthogonal to your question about the benefits of functional programming. Let me give you a simple example of macro: a macro print-variable that takes a variable and prints: "$variable = $value", where $variable is the variable name and $value its corresponding value. I wrote it on my Clojure REPL, and run it that way:

    user> (def x 4)
    #'user/x
    user> (print-variable x)
    x = 4
    nil 
In a language without macros, the function print-variable wouldn't have access to the string "x" once x has been defined. In a language with eval, you could do something like:

    def print-variable(variable_name):
        value = eval(variable_name)
        eval("print("+variable_name+"="+value+")")
But you would have to call that function with a string, which is not as elegant and can lead to errors as the string could be misspelled.

Now, I will show you the code of the macro in Clojure:

    (defmacro print-variable [variable]  
         `(println '~variable "=" ~variable))
What is does is take the variable you want to print, and defer its evaluation (~variable) only when you want to know what value is assigned to that variable. In the meantime, it can get the name of the variable ('~variable).

Here is what it does when I expand the macro:

    user> (macroexpand '(print-variable x))
    (clojure.core/println (quote x) "=" x)
The clojure compiler transforms the macro into the expanded code, and then injects it into the rest of the code.

Hope this helps.

Your Python example wouldn't work. eval is still scoped lexically. You'd need to get current stack frame, get locals from the previous frame and only eval within these locals. That's what TCL `upvar` does, BTW.

Macros let you avoid this kind of problems when meta-programming (but then they bring their own problems: hygiene, for example).

Good point, I haven't really thought the Python example through. I can't find a quick fix now, so I leave it as an illustrative code (even that failing code is an inferior version of the macro, so...).
I can't seem to find it now, but I remember reading an essay, probably by Joel Spolsky -- on mentoring a newly hired junior Cprogrammer. The programmer had implemented some functionality in C, and had written lots of tiny function calls, with descriptive names, that composed to form the solution. The author called the programmer into the office, and berated the coding style, citing "efficiency" as a reason for why the program was an example of poor coding style.

The main point of the article, was that the junior programmer was right: well structured, self-documenting code is always better than fast code. If it ends up being too slow, and (in the case of C) can't be fixed with "inline" and "-O2", then, and only then, move towards a different, hopefully more efficient solution.

Now, obviously, writing functional-style C isn't the only (or perhaps even the best) way of writing structural, clean, C code.

But functional composition, especially with help from the underlying programming language, can be compact, and very easy to reason about.

Never mind the fact that approximately half of Haskell (and some clojure?) programmers appear to think that "f a b" is more readable than "scaleVector Vector Factor". Conciseness can be a weakness as well as a strength.

One main goal of functional programming is writing less error prone code. When mapping a function over a sequence there's no way to get an off by one error, or any other bug associated with typical for loop.

Not a lot of aspects of functional programming are tied to the code is data idea (formally called homoiconicity). However when writing Clojure, the user is actually inputting Clojure data structures. Something I've noticed is structural navigation inside the editor has beneficial. I can edit Clojure code so much faster than any algol-family (C-like) language I've used. And while I find it important to spend more time thinking than typing, it's easier for me to stay in flow when the code is so malleable.

A dense functional language like Clojure or Haskell has about a 10x reduction in lines of code once you get rid of all the OO boilerplate and replace it with composable, higher-order functions. I used to write c++ for a living and when I switched to Clojure, my days are still full of the same level of development work, but the amount of code that I actually write is dramatically less. In a functional language, a lot more goes into the thought process of how to construct algorithms rather than in meeting the demands of the OO environment you work in. In some languages you have no choice but to make classes for even the tiniest of tasks, which is a limiting requirement to put in the hands of someone trying to creatively solve a problem from any angle without preconceived design patterns influencing the approach. FP instead lets you concentrate on other matters and usually use your brain for the fun stuff more often.
Have you read "Why Functional Programming Matters"? https://www.cs.kent.ac.uk/people/staff/dat/miranda/whyfp90.p...
Rich Hickey had a talk (not sure which one it is at the moment) about how we used to refer to programmers as data processors as the core of your job was reading data from somewhere, processing that data, then moving that data somewhere else. We've moved away from that definition but the fact is, at its core, we are still data processors.

Once you've convinced yourself of that and you look at Clojure through that lens you will find immense power in that language, as everything is built around data transformations.

I do think the move away from data processors as a title is more of an ego thing...none of us probably wants to be called a data processor.

In a nutshell:

- Functional programming style tends to make your programs easier to reason about, as it avoids state/mutation. This also makes testing your programs much easier, and can also make concurrency easier.

- It's a different way of thinking. Some problems are much easier to solve in a functional fashion, some are much easier in a imperative fashion.

- Functional code can increase code re-usability as it decouples data from its operations

- A lot of functional languages have very advanced type systems compared to say, Java or C++. I'm personally a big fan of static typing and after learning F# I am sorely missing its type system in C#

I had many revelations while reading and doing the exercises of the SICP book.
I think the idea behind homoiconicity is that you can generate and modify code using the same tools you would use on data; not string functions and regular expressions to manipulate the text you would call eval() on. Trying to understand semantics of code blocks this way is like parsing html with regular expressions. I used to modify built-in functions in emacs lisp by calling a modifying function (though later I was told the canonical way to do it was just to copy the function elsewhere, modify it, and evaluate it after the built-in was loaded).
> Many languages have eval() where data can be treated as code.

That's significantly less powerful if you lack structured ways to compose things together before evaling them.