←back to thread

296 points gyre007 | 1 comments | | HN request time: 0.209s | source
Show context
lewisjoe ◴[] No.21280702[source]
Richard Gabriel’s famous essay “Worse is better” (https://www.jwz.org/doc/worse-is-better.html) is an interesting perspective on why Lisp lost to C. In a way, the same arguments (simplicity vs consistency vs correctness vs completeness) can be made for why functional programming lost to OOP.

But those philosophical perspectives aside, personally I find my brain works very much like a Turing Machine, when dealing with complex problems. Apart from my code, even most of my todos are simple step-by-step instructions to achieve something. It’s easily understandable why like me, other non-math folks would prefer a Turing Machine over Lambda Calculus’ way of writing instructions.

This could be why OOP/Imperative was often preferred over FP.

replies(13): >>21280720 #>>21280760 #>>21280800 #>>21280835 #>>21280855 #>>21281061 #>>21281225 #>>21281281 #>>21281464 #>>21282667 #>>21283371 #>>21286296 #>>21288188 #
strangenessak ◴[] No.21280835[source]
> personally I find my brain works very much like a Turing Machine

Exactly this. How baking a cake in FP looks like:

* A cake is a hot cake that has been cooled on a damp tea towel, where a hot cake is a prepared cake that has been baked in a preheated oven for 30 minutes.

* A preheated oven is an oven that has been heated to 175 degrees C.

* A prepared cake is batter that has been poured into prepared pans, where batter is mixture that has chopped walnuts stirred in. Where mixture is butter, white sugar and brown sugar that has been creamed in a large bowl until light and fluffy

Taken from here: https://probablydance.com/2016/02/27/functional-programming-...

replies(13): >>21280936 #>>21280977 #>>21281011 #>>21281055 #>>21281385 #>>21281396 #>>21281653 #>>21281843 #>>21281990 #>>21282883 #>>21283119 #>>21283649 #>>21283658 #
6gvONxR4sf7o ◴[] No.21283658[source]
Okay, so first of all this is an excellent joke. But it's not that great of an analogy.

This quote chooses one of many FP syntaxes. It's cherry picking. It uses "a = b where c = d." That's equivalent to "let c = d in a = b." Let will allow you to write things like:

    let
        cake_ingredients = [butter, white sugar, sugar]
        batter = cream(ingredients=cake_ingredients,
                       dish=large_bowl,
                       condition=LIGHT_AND_FLUFFY)
        prepped_pans = pans_full_of(batter)
        oven = preheat(your_over, 175 C)
        cake = bake(cake, 30 minutes)
    in
        dessert_tonight = cooled(cake)
This isn't where FP and imperative are different.

What's really different is that the let statement doesn't define execution order. That's not so relevant to this part of the mental modeling though.

I think it's great that I can choose between "let ... in ..." or "... where ...". In real life, for a complex bit of language, I happen to often like putting the main point at the top (like a thesis statement), then progressively giving more details. Mix and match however's clear.

replies(1): >>21283852 #
falcolas ◴[] No.21283852[source]
Perhaps it's the analogy leaking, but in baking, order of operations matters, and some operations must be done in parallel (pre-heating, based on initial oven state) to produce a good end product.
replies(1): >>21286114 #
nybble41 ◴[] No.21286114[source]
Yes, and this is one of the areas where functional programming really shines. An imperative program is defined as a series of ordered steps and the compiler can't (in general) reorder steps to optimize use of resources because the steps could have arbitrary side-effects.[1] The FP version is essentially a dependency graph which constrains the order of operations without mandating a specific final order. The pre-heated oven is needed for baking but not for the batter, so these parts can automatically be evaluated in parallel just by enabling the multithreaded runtime.[2]

[1] Certain primitive operations can be reordered but that depends on the compiler having access to the entire program. A call to a shared library function is an effective optimization barrier for any access to non-local data due to potential side effects.

[2] For the purpose of this example I'm assuming the unused `oven` variable was meant to be passed in to the `bake` function.

replies(1): >>21287178 #
nice_byte ◴[] No.21287178[source]
> the compiler can't (in general) reorder steps to optimize use of resources

i'm not sure what you mean by that because compilers reorder instructions to improve performance all the time (and CPUs do it dynamically too).

replies(2): >>21292222 #>>21292272 #
1. nybble41 ◴[] No.21292272[source]
I mean that an imperative program spells out a particular order of operations and the compiler is forced to reverse-engineer the dependencies based on its (usually incomplete) knowledge of each step's side effects. When the potential side effects are unknown, such as for calls to shared library functions, system calls, or access to shared global data, or any call to a function outside the current compilation unit in the absence of link-time optimization, then it must preserve the original order even if that order is less than optimal.

The kind of reordering you see in imperative programs tends to be on the small scale, affecting only nearby primitive operations within a single thread. You don't generally see imperative compilers automatically farming out large sections of the program onto separate threads to be evaluated in parallel. That is something that only really becomes practical when you can be sure that the evaluation of one part won't affect any other part, i.e. in a language with referential transparency.