> 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…
Had GPT-4 clarify some of the syntax for me https://poe.com/s/uj80au2i4TuNWgUa1pNm
Mindset shifts for functional programming (with Clojure)
51–60 of 100 posts
Re: Mindset shifts for functional programming (with Clojure)
#52> 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…
> Nobody has yet convinced me that recursion has any sustained advantage over looping. most sql systems support recursive queries. I believe recursive code can be analyzed by the system and executed in a the most efficient manner. Loops have sideffects closely linking them to actual execution, which makes them blackboxes to the system.
https://www.postgresql.org/docs/current/queries-with.html#id...
Note how there is no way to remove a row from the result set once it has been added. That would not be the case with a truly recursive query, because you would be constructing a new result set at every step. As a concrete example, try to write a graph query which finds all nodes exactly three edges from some starting node. That would be trivial with true recursion, but is impossible with a recursive CTE alone.
Re: Mindset shifts for functional programming (with Clojure)
#53> 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…
>Nobody has yet convinced me that recursion has any sustained advantage over looping. Recursion gives you a stack by default. You don't have to explicitly think about the stack. In looping the stack must be explicit. Recursion and looping are the same thing. Recursion can be mechanically translated to a for loop and a stack, the concepts are isomorphic.
Re: Mindset shifts for functional programming (with Clojure)
#54The advantage of clojure is in data centricity. It is not about shifting mind to recursion from looping. Clojure has a 'for' which is it's list comprehension loop. The actual fun of clojure: https://bitslap.it/blog/posts/fun-of-clojure.html
Clojure data type's are fantastic, but the main thesis of my post isn't what's required for FP in Clojure, rather, what's required to become comfortable with pure functional programming concepts which is why I reference Haskell a lot in the post. The examples just happen to be in Clojure. Sorry for the confusion.
Re: Mindset shifts for functional programming (with Clojure)
#55Earlier 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…
for(range(n)): is_true = !is_true And adjust for all the off by 1 errors. You're managing the same amount of state both ways, but with the loop all the state mutation lives on one line instead of spread throughout a stack.
The biggest problem is that, assuming we copy your snippet into a couple of function definitions, we've completely lost the encapsulation/separation-of-concerns/delegation/etc. provided by my `odd` and `even` functions; i.e. all of the "software engineering" stuff that makes source code more maintainable than disassembled binary.
Your loop is more like the following, which is not what I wrote:
even(n: uint): boolean = n match {
case 0: true
case n: !even(n-1)
}
odd(n: uint): boolean = n match {
case 0: false
case n: !odd(n-1)
}
The major difference is the use of iteration/tail-recursion in this implementation:- This loop collapses all of the abstraction, forcing each function to implement the entire solution. In contrast, my implementation uses divide-and-conquer: only the zero case is handled directly, and the non-zero case is delegated to a more suitable handler.
- Wrapping two copies of this loop into `even` and `odd` functions will completely lose the relationships inherent in my implementation. For example, if we add instrumentation, optimisations, logging, etc. to the `even` function, that will affect my `odd` function but have no effect if we were to write independent loops.
- This looping implementation has extra dependencies, specifically on `range` and `!`. The `!` function requires knowledge of boolean algebra, the `range` function requires knowledge of lists/sequences/iterators, and `range` also seems to make more sophisticated use of number theory than the `odd`/`even`/`-` required to understand and maintain my implementation.
Note that I'm not claiming either of these is "better"/"worse" than the other. Simply that your loop is not representative of my example; that's specifically why I chose a mutually-recursive example, and not an iterative/tail-recursive one!
(Of course, all of these are exaggerations for such a simple example; but complex, real-world codebases require such engineering practices and tradeoffs to be taken seriously for the sake of maintenance)
Re: Mindset shifts for functional programming (with Clojure)
#56I think the main thing is not really FP vs whatever else but the most fundamental thing is declarative vs imperative. That is probably the biggest jump in mindset when coming from e.g. C or C++ to something like Haskell - that instead of telling the computer/compiler what to do, you describe what you want.
You are still telling the computer what to do, describing what you want is more like prolog. The main difference between Haskell and C in a case like this would be the level at which you tell it what to do.
Re: Mindset shifts for functional programming (with Clojure)
#57They occupy the same space as Java's streams, but manage to do the same stuff with a smaller, more generic, more extensible interface, that can do more stuff (eg intermediate stages get told when input is finished). I'm jealous.
EDIT: IIUC, in Java terms, Clojure's "reducing functions" are like Collector, and a transducer is a function which takes a Collector and returns another one. There is then a little bit of top-level sugar so you can give a sequence of transducers, terminating in a concrete reducing function, and get back a reducing function which itself feeds things through the chain of reducing functions built by the transducers. You could probably replicate this in Java, but it might be too clunky even for Java programmers.
Re: Mindset shifts for functional programming (with Clojure)
#58I am not a Clojure programmer, and am pretty skeptical of LISPs and FP more generally, but i have to say that transducers are pretty great. The descriptions of them, and the way the interface is expressed, are a bit off-putting, but once you grok them they're actually simple and useful. They occupy the same space as Java's streams, but manage to do the same stuff with a smaller, more generic, more extensible interfac…
They were an small obsession of mine last week as I wrote an accompanying blog post to demystify them.
Re: Mindset shifts for functional programming (with Clojure)
#59> 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…
Maybe partly because ordered sets are often implemented as trees, which are definitely easier to traverse recursively.
I find recursion nicer to work with mentally; continuous rather than discrete, with less edge cases to consider.
Re: Mindset shifts for functional programming (with Clojure)
#60I think the main thing is not really FP vs whatever else but the most fundamental thing is declarative vs imperative. That is probably the biggest jump in mindset when coming from e.g. C or C++ to something like Haskell - that instead of telling the computer/compiler what to do, you describe what you want.
I think this would be correct for declarative programming languages, but I don't agree that Haskell is a declarative programming language. Haskell is pure functional programming in my mind. A declarative programming language might be something more akin to DML SQL for a RDBMS, or HCL for Terraform (pre-looping, v0.X).