Hacker News new | past | comments | ask | show | jobs | submit login

Ok, came up with another example. See how let:

    (let [a 1
          a (+ (inc a) (inc a))
          _ (println a)
          a (+ a a)]
     a)
Can be rewritten simply as a nested composition of anonymous functions:

    ((fn[a]
      ((fn[a]
        ((fn[_]  
          ((fn[a]
            a)))
         (println a))
        (+ a a))
       (+ (inc a) (inc a))))  
     1)
The latter representation you would consider functional programming no? Well the let is just syntactic sugar for it and can be rewritten in terms of only composed anonymous functions (aka lambdas).

And like I said previously, you can reduce it with variable substitution, which gets rid completely of all the variables, at compile time, and the evaluation will give the same result:

    (do
     (println (+ (inc 1) (inc 1)))
     (+ (+ (inc 1) (inc 1))
        (+ (inc 1) (inc 1))))
From the let form, or from its corresponding anonymous function form:

    ((fn[_]  
      ((fn[]
        (+ (+ (inc 1) (inc 1))
           (+ (inc 1) (inc 1))))))        
     (println (+ (inc 1) (inc 1))))
In this case you see more clearly that the side-effect causes impurity, since it can't really be reduced, it's only in this case that order dependence matters, and so we can't eliminate the wrapping function, and this relies purely on Clojure's left to right argument evaluation ordering which allows you to mix/match side-effects within pure functions with predictable effect timing.

So here what happens you reduce that function into a do-block (which is Clojure's imperative form):

    (do
     (println (+ (inc 1) (inc 1)))
     ((fn[]
       (+ (+ (inc 1) (inc 1))
          (+ (inc 1) (inc 1)))))) 
And now you can further reduce the pure parts, which takes us back to what we had when we reduced the let:

    (do
     (println (+ (inc 1) (inc 1)))
     (+ (+ (inc 1) (inc 1))
        (+ (inc 1) (inc 1))))
And finally this can be reduced further:

    (do
     (println (+ 2 2))
     (+ (+ 2 2)
        (+ 2 2)))

    (do
     (println 4)
     (+ 4
        4))
To our most reducible form:

    (do
     (println 4)
     8)
This reduction can all happen in parallel or in any order, and the result will always be the same.

Ok, and this last bit is very important, this is what people mean when they say that in functional programming the order of execution doesn't matter. The side-effects must still be sequenced in their correct order, but all the computation can happen in arbitrary order, because the computation doesn't rely on a sequence of instructions like it does in the imperative programming paradigm, instead it relies on this "reduction" process I described which as you see you are free to reduce each part in whatever order you want, you'll always end up with the same thing in the end.




Thanks for your willingness to engage and instruct on this. I've learned a lot. Cheers!


It helps me to have my understandings challenged, I could have been wrong, and going through trying to explain myself helps with me better understanding things too, so cheers to you as well!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: