3 ms·
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 use
by 6gvONxR4sf7o 7y ago
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.
- falcolas 7y agoPerhaps 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.
- nybble41 7y agoYes, 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.
- nice_byte 7y ago> 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).
- repolfx 7y agoCompilers and CPUs only reorder over tiny instruction windows. He's talking about re-orderings over enormous windows, in a way that requires whole program analysis. But that doesn't really happen in reality. FP languages promised auto-parallelisation for decades and never delivered. Plus you can get it in imperative languages too - like with Java's parallel streams. But I never see a parallel stream in real use.
- nybble41 7y agoIt's not completely automatic but it is fairly close. If you enable the threaded runtime then "sparks" will be evaluated in parallel. You do have to say which expressions you want evaluated as separate "sparks" with the `par` operator but that's it—the runtime manages the threads, and common sub-expressions shared by multiple sparks will typically be evaluated only once. There are no race conditions or other typically concurrency issues to worry about since the language guarantees the absence of side effects. (That is the biggest difference between this and Java's parallel streams: If the reduction operation isn't a pure function then the result is undefined, and there isn't anything at the language level in Java to enforce this requirement.) EDIT: An example of effective parallelism in Haskell: import Control.Parallel (par) fib n | n < 2 = 1 | n >= 15 = b `par` a `seq` a + b | True = a + b where a = fib (n-2); b = fib (n-1) main = print $ map fib [0..39] Note that the implementation of `fib` has been deliberately pessimized to simulate an expensive computation. The only difference from the non-parallel version is the use of `par` and `seq` to hint that the two operands should be evaluated in parallel when n >= 15. These hints cannot change the result, only the evaluation strategy. Compile and link with "-threaded -with-rtsopts=-N" and this will automatically take advantage of multiple cores. (1C=9.9s elapsed; 2C=5.4s; 3C=4s; 4C=3.5s)