Live data from Hacker News

Mindset shifts for functional programming (with Clojure)

blog.janetacarr.com

81–90 of 100 posts

Re: Mindset shifts for functional programming (with Clojure)

#81

Earlier quoted context omitted.

It doesn't make any sense to convert any arbitrary recursion into tail called optimized. If the recursion can be tail called optimized then yes the loop is the optimized performant implementation. But if the recursion fundamentally utilizes the call stack then the reverse is actually true. The recursion is now the performant implementation of a for loop. So a loop conversion optimization actually doesn't make sense h…

Works for O(log n) stack depth. In general, recursion is not viable.

This is quite false. Languages like Scheme (dialects), ML variants, Erlang and Haskell all make use of recursion in general and they produce very viable results.

Re: Mindset shifts for functional programming (with Clojure)

#82
post #15

> Recursion over Looping Part of what makes Clojure a great programming language is that you don't have to believe this if you don't want to. Nobody has yet convinced me that recursion has any sustained advantage over looping. Using a loop is generally bad practice if a more specialised operation is available (don't loop if something is a simple map or reduce for example). But if the situation justifies a recursion t…

I mostly agree, but one really nice use case for recursion is when dealing with trees. For example, writing a function parse_object() that recursively calls itself to parse child objects is way more pleasant than manually managing your own stack, especially if the tree has many kinds of objects and many branches.

Unfortunately in most languages this pattern will lead to stack overflow on medium-sized inputs, so you can't often use it unless you're using a language like Racket or Erlang which can't really stack overflow.

Re: Mindset shifts for functional programming (with Clojure)

#83
As a Clojure programmer, I find it a bit unfortunate that most of this post, like many other posts demonstrating Clojure, tend to paint a rosey picture and never mention the realities one has to reconcile in a Clojure codebase.

For instance, one of the sections in this blog post is titled "Functions over Objects" but any Clojure programmer with experience will agree that most code ends up becoming some sort of map munging. Rather than "functions over objects" you really have "map-munging and function instrumentation in dev over banging on concrete instances of classes".

There is also this statement

> functional programming languages often facilitate iteration through recursion

then the author proceeds to mention loop/recur, which is nice, but it is dishonest at best and lying at worst to imply loop/recur is the most natural or common way we iterate over a structure. Off the top of my head, I always see idioms like `(for [[k v] some-map] (do things with k v))`. It would also be nice to demonstrate how faux-recursive looks like with loop/recur rather than stating Clojure's lack of TCO and not elaborating further on how the idiomatic version of `recursive-map` would look like.

Next,

> by (nearly) eliminating side-effects, functional programming (nearly) elminates this whole class of bugs.

That may be the intent of purely functional code in general, but in Clojure one regularly interoperates with the host, to take advantage of their rich ecosystem. Many of the libraries one will interop with will involve some imperative or stateful things! So I think it'd be better if the author had mentioned the idea of "functional core, imperative shell." That is a pattern most Clojurists would agree with, and it is what people really do in a code base, or at least attempt to design.

Lastly, while Clojure allows one very naturally to mock up a finite state machine with a single map and writing a few functions to describe transitions given the current state of the machine, I want to emphasize that not much Clojure codebases I've seen ever used FSMs explicitly :-)

Overall, the article is okay, but my brain registers it as another "Clojure portrayed with rose-tinted glasses and contrived examples" article. Not that this is bad. It's just not the kind of sales pitch I'd want to show to non-Clojure programmers, because they will likely want to ask questions about maintainability, testing, instrumentation, etc. And it is possible to show Clojure being nice for each of these things. At a previous job I got to see ClojureScript code dating back to 2014, untouched, and surviving 8 years worth of language and library updates.

Re: Mindset shifts for functional programming (with Clojure)

#84

Earlier quoted context omitted.

Vector operations that perform the same fixed addition to each element, yes, but that's not really the same thing. And yes I'm very much in agreement that the real power of recursive algorithms is when working with tree-like data structures.

Your question was, and I quote: > is there actually any chipset with the algorithmic ability to add more than 2 numbers altogether in one operation (without having to store intermediate results) I was just answering your question. I am not responsible for quality of your questions. What you do is you posted a question but had something else in mind. You got an answer but that answer does not match the question you ha…

My understanding is those vector operations don't , in fact, add more than 2 numbers together, but rather simultaneously add lots of pairs of numbers. Happy to be corrected and no berating intended.

Re: Mindset shifts for functional programming (with Clojure)

#85
post #25

Earlier quoted context omitted.

Out of curiosity is there actually any chipset with the algorithmic ability to add more than 2 numbers altogether in one operation (without having to store intermediate results)? I'd also think looping is far closer than recursion to how we tend to manually calculate things (either entirely in our heads or using a pen & paper or even a basic calculator). (Edit: looks like a number of other posters have made the same…

> Out of curiosity is there actually any chipset with the algorithmic ability to add more than 2 numbers Perhaps? But this would be like asking if there was a chipset that can work without branching because of someone mentioning "GOTO bad". > I'd also think looping is far closer than recursion to how we tend to manually calculate things It's not the looping I mind, it's the mutation.

If there were such an ability at the machine language level, it would be a strong argument against expressing the addition of a set of numbers using either a loop or recursion. I don't really see that either manner of expressing such an operation is always preferable, and would rather simply use a built-in language or feature (e.g. reduce) that the compiler/interpreter is free to translate into the most appropriate machine instructions for the given hardware.

Re: Mindset shifts for functional programming (with Clojure)

#86

If your team and you can read it later or when it goes wrong, then it's fine. I don't want to need to think hard about what code does, it should be clear. Or jump through 100 files to find a simple algorithm that has been divided into 100 pieces. I think people refactor to their own understanding or mental model or refactor-to-understand. I think there are cases where imperative code is intuitive and others where fun…

Agreed, cyclomatic complexity is definitely something to be aware of when designing functional programming systems. Although, I disagree about Clojure's readability, but that's probably because I've been doing it for so long. Interesting Java project you've got there. Reminds me of Clojure's core.async library and software transaction memory ;) (though, I know it's not exactly the same thing). If I was transforming l…

I am really interested by Software transactional memory and enjoyed reading the Joe Duffy's blog posts about Midori and adventures with STM.

https://github.com/joeduffy/joeduffy.github.io/blob/master/_...

I like left-right concurrency control because it sidesteps a number of thread safety problems by ensuring that a thread can always safely read or write to its own buffer.

Re: Mindset shifts for functional programming (with Clojure)

#87
post #2

> Transformations over Instructions Hell yeah! The problem with "functional" programming is that a lot of people simply don't get you want to push as much of your program to be generic tools that transform things (ie FUNCTIONS!) When I start on a new problem I typically look at what kind of tools (functions) would make the solution concise and readable. Then create the tools and then write the solution with the tools…

This “recursion is more complicated” idea is a myth.

Yes, a lot of people seem to have trouble with it, but it isn’t fundamentally more complex or difficult. The hardest thing about learning recursion is unlearning the other patterns. It’s totally a matter of familiarity.

Re: Mindset shifts for functional programming (with Clojure)

#88
post #50

Earlier quoted context omitted.

I'm not going to argue this is good, but it's fairly analogous: boolean isEven(int n) { boolean even = true; for (; n > 0; --n) even = !even; return even; }

Your implementation has lost the encapsulation of mine, and broken the call-graph relationships. For example: - Updates and bug-fixes to the `isOdd` function will not be inherited by your `isEven` function. - If I set a breakpoint in this function, it won't get triggered when I call `isOdd`. - Your `isEven` function requires an implementation of `!` - etc.

> - Updates and bug-fixes to the `isOdd` function will not be inherited by your `isEven` function.

> - If I set a breakpoint in this function, it won't get triggered when I call `isOdd`.

    boolean isOdd(int n) {
      return !isEven(n);
    }
> - Your `isEven` function requires an implementation of `!`

Yes, but this language has one, so that's fine.

Re: Mindset shifts for functional programming (with Clojure)

#89

Earlier quoted context omitted.

> Nobody has yet convinced me that recursion has any sustained advantage over looping. Looping may require trampolining or defunctionalisation, whilst recursion can be written much more directly and simply. As a very simple example (in pseudocode): even(n: uint): boolean = n match { case 0: true case n: odd(n-1) } odd(n: uint): boolean = n match { case 0: false case n: even(n-1) } Whilst these are pretty silly implem…

Many of the replies to this comment seem to have focused on the problem domain (numbers and booleans); and missed the main feature I was trying to show about recursion, which is a collection of functions delegating sub-tasks between themselves (rather than e.g. trampolining via a "main loop") A closer analogy to my code would be something like this: the domain logic is still abstracted and encapsulated into separate…

FWIW for my comment I essentially used your code and cleaned up compiler-generated imperative code.

So yes it used the fact that it only recursed to n-1 but did not use any optimizations related to booleans. And looking at other examples can see general principles. You store a data structure for each function which acts as a cache. For example, suppose you said even(0) = True, even(1) = False, and even(n) = even(n - 2), then one way would to unroll it would be to use a size 2 ring buffer as a cache.

Re: Mindset shifts for functional programming (with Clojure)

#90
post #15

> Recursion over Looping Part of what makes Clojure a great programming language is that you don't have to believe this if you don't want to. Nobody has yet convinced me that recursion has any sustained advantage over looping. Using a loop is generally bad practice if a more specialised operation is available (don't loop if something is a simple map or reduce for example). But if the situation justifies a recursion t…

Looping, as in using a loop keyword, is synchronous. Recursion is also a loop, but can iterate asynchronously as necessary. That is the primary advantage.

Recursion doesn't do that by default, though. And if you have to alter your code to run recursive fns on a new thread or executor, nothing prevents you from doing something similar with a loop, either.
Post reply on HN