
Programming and thinking the functional way - peteratt
http://peteratt.com/programming-thinking-functional-way/
======
tel
I still remember the first time I started to grok recursive definitions in a
functional language. At the time it was Scheme and I remember going through
all the effort to track the various bits of state and operation as they
"reboot" and begin again in each recursive call.

It made me think that recursion, no matter how short the code looked, was
terrible.

Now I realize that recursion, thought about properly, requires so much less
mental overhead. All those lines of code that vanished were really whole
concepts and ideas which could vanish as well.

Of late, my style has been refined by working with theorem provers which make
the ultimate connection. Recursion is absolutely nothing more than
mathematical induction. Each recursive call is just you invoking the inductive
hypothesis.

What that connection really drives home is that when writing a recursive
definition you need to do so very few things. First, you need to make sure
your definition has a good foundation—you must list all of the conditions
where it terminates and ensure that they truly found and support the
algorithm/proof. Then you must write the inductive engine that will burn away
whatever inputs you receive down to the foundation you just made.

The release is in noticing that those inductive engines can be primed on the
very tiniest actions—and those tiny actions are all you, the implementer, are
responsible for.

When writing your inductive step consider only the things that are novel about
_this case_. If I'm working with an input list and inducting on `cons` then
all I must do is consider what happens to the head and tail of the list.
Everything else will be handled by the turning of the inductive engine.

Now a good algorithm just feels like someone walked me into a room with a
gigantic domino structure. I walk around it slowly and determine the ends and
then just flick the single domino which will churn away inevitably the rest of
the algorithm.

~~~
lomnakkus
Interesting! I had the exact same experience where recursion _really_ clicked
-- proof by induction === recursion. Obviously (in hindsight)! :)

And, by extension, structural induction === sum & product types + pattern
matching destructuring for recursive functions. The fact that the type checker
can make sure you've got your base cases covered is just gravy.

~~~
platz
Although I think loops and recursion aren't so different: here's a snippet
from meijer:

"The goal of recursion and loops is exactly the same, you want to define
something in terms of itself.

That's what the loop does; it repeats a computation and something gets
smaller...

the loop variable or when you for each over a loop you're picking out the next
element..

That's exactly what a recursive definition tries to do, you're trying to
define a function, in terms of a smaller version of it's argument."

[http://channel9.msdn.com/Series/C9-Lectures-Erik-Meijer-
Func...](http://channel9.msdn.com/Series/C9-Lectures-Erik-Meijer-Functional-
Programming-Fundamentals/C9-Lectures-Dr-Erik-Meijer-Functional-Programming-
Fundamentals-Chapter-6-of-13)

~~~
tel
Loops are generally special cases of recursion. You can always derive an
iterator from a recursor by forgetting things, but you need product types or
ambient state to go the opposite way.

In Turing complete languages they're both special cases of fixed-point
equations, though, so in that sense they both have the same goal.

------
rdtsc
Another practical functional language is Erlang.

It is at the core of many mobile to internet gateways out there. At the core
of WhatsApp. Some databases (Riak, CouchDB) and message queues (RabbitMQ).

Language-wise, besides concurrency constructs, you get pattern matching,
immutable data structures (and bindings). Unlike Haskell, all types are
dynamic (but strong).

Also a counterpart to Learn You A Haskell For Great Good is Learn You Some
Erlang For Great Good:

[http://learnyousomeerlang.com/syntax-in-functions#pattern-
ma...](http://learnyousomeerlang.com/syntax-in-functions#pattern-matching)

For example the quicksort sample would look like:

    
    
        sort([Pivot|T]) ->
          sort([ X || X <- T, X < Pivot]) ++
          [Pivot] ++
          sort([ X || X <- T, X >= Pivot]);
    
        sort([]) -> [].
    
    
    

But it is the large applications that ends up breaking my brain. I am
convinced once you learn programming in an imperative or object oriented
language you brain just become molded to that model of thinking and it is very
hard to adjust (it is nevertheless very useful to try).

~~~
CocaKoala
I had to audit a web app and the back end code was all written in Erlang. I
had never seen the language in use before, so it took me a little while to get
the hang of reading it.

Now that I've spent some time playing around with it, I think I like Erlang
better than I like Haskell; I used to think I disliked dynamic typing but it
turns out I just don't like the way Ruby handles it. Erlang feels really good
to write code in, and it feels a bit less finicky than Haskell is.

If anybody wants to try functional programming and feels like they just can't
get along with Haskell, I'd definitely recommend you give Erlang a try. It's
not great at everything but I'd highly recommend it as something to try.

~~~
lomnakkus
There's no accounting for taste, but as "jerf" posted in a sibling comment
Erlang's not really functional[0]. All that's really happening is that the
mutable state is "hiding" in the message passing portion of the application.
Just as an example: It's pretty simple to implement a mutable reference cell
as an actor which contains only pure functional code. I'm not sure where I
first saw it demonstrated, I think it was one of Erik Meijer's talks/videos.

[0] I mean in the "no/minimal mutable state" sense.

EDIT: Removed potentially confusing aside.

EDIT#2: I guess I should elaborate: The idea is that you just have the
MutableRef actor accept two messages: Set(X), Get(X). The basic idea was to
just have the actor continually send itself Set(X) messages with the current
value -- thus exploiting the messaging to keep mutable state.

~~~
felixgallo
Of what use is this distinction, especially when zero languages fall within
its definition? Honestly.

~~~
lomnakkus
Hm? Haskell is purely functional. (Alright, you can theoretically use
unsafeCoerce and unsafePerformIO, but in practice it doesn't actually happen.)

~~~
felixgallo
Can you please point me to any real-world Haskell program that is purely
functional? I am interested in seeing what a program without I/O looks like.

~~~
lomnakkus
All of them. Remember that "IO a" is just a value. :)

It's really the "interpreter" of the IO monadic values which causes all the
side effects. I am aware that there are some issues with this interpretation,
but if you want to get pragmatic it's really a question of X% pure/(100-X)%
impure while keeping X as close to 100% as possible. Haskell can do that for X
arbitrarily close to 100% (depending on how much abstraction/effort you're
willing to go through).

------
cousin_it
It boils down to the choice between these two alternatives:

1) If it's simple to imagine the computer doing something, it should be easy
to program.

2) If it's simple to analyze something mathematically, it should be easy to
program.

The first path leads to C-style programming with pointers and loops. The
second path leads to lazy pure functional programming and beyond.

Personally, I'm in the first camp. Small functional programs are often shorter
and more beautiful than their imperative and OO counterparts, but large
functional programs quickly accumulate so many abstractions that they become
incomprehensible to me, more so than large imperative or OO programs.

Maybe that just says something about my intelligence, or maybe the functional
camp hasn't yet figured out how to write large programs in a readable way.
Case in point, see the list of operators defined by the popular Lens library
in Haskell:
[http://hackage.haskell.org/package/lens-3.8.5/docs/Control-L...](http://hackage.haskell.org/package/lens-3.8.5/docs/Control-
Lens-Operators.html)

~~~
chongli
_Maybe that just says something about my intelligence, or maybe the functional
camp hasn 't yet figured out how to write large programs in a readable way._

Haskell programmers tend not to write large programs, period. Instead, we
build lots of libraries until our problem is trivial to solve.

 _Case in point, see the list of operators defined by the popular Lens
library_

Not really a fair example. Lens is not a program (large or otherwise), it is a
new programming language embedded within Haskell. Viewed within that context,
its bevy of operators is somewhat justified. The other thing to note about it
is that lens was designed specifically to avoid clashing with variable and
operator names defined in other popular libraries so that users could import
it unqualified.

~~~
dragonwriter
> Haskell programmers tend not to write large programs, period. Instead, we
> build lots of libraries until our problem is trivial to solve.

So, how is that different from any other language since the invention of
functional/procedural decomposition? Programmers _generally_ write libraries
and then compose them, rather than writing large monolithic programs,
irrespective of language.

~~~
nardi
I think he's saying that Haskell programmers do this more than other
programmers. I agree.

------
keeg
Writing quicksort non-functionally doesn't HAVE to be unreadable. Not that I
disagree with Haskell being great and all, I just think it's important to
realize that imperative != ugly code.

    
    
      public class QS {
      	public List<T> Quicksort<T>(List<T> l, Comparator<T> comp) {
      		if (l.size() <= 1) {
      			return l;
      		}
      		
      		List<T> lesser = new ArrayList<T>();
      		List<T> greater = new ArrayList<T>();
      		
      		T pivot = l.get(0);
      		
      		for (T t: l) {
      			if (comp.compare(pivot, t) <= 0) {
      				lesser.add(t);
      			} else {
      				greater.add(t);
      			}
      		}
      		
      		List<T> out = new ArrayList<T>();
      		out.addAll(Quicksort(lesser));
      		out.addAll(Quicksort(greater));
      		
      		return out;
      	}
      }

~~~
tromp
This will recurse forever when sorting the list [1,0]. Which is why you need
to take out the pivot to ensure that the lists you recurse on get smaller and
smaller...

------
eoconnell
"Wow. i's and j's all the way, what is this? Why is it so long compared to the
Haskell example? This looks like comparing C and assembly 30 years ago! And in
some respects, it is the same leap."

I don't think this is a fair comment considering the in-place Haskell
implementation isn't incredibly readable either.

~~~
the_af
Agreed. I think his overall argument still has merit, but the specific example
isn't very good, because Java's quicksort implementation __is __in-place. He
isn 't comparing equivalent programs.

------
ridiculous_fish
I'm not sure if I'm the first to notice, but the Haskell quicksort function is
wrong, because it mishandles NaN:

    
    
        main = let nan = 0.0 / 0.0 in
                do putStrLn $ show $ quicksort [nan, 1.0, 2.0, 3.0]
                   putStrLn $ show $ quicksort [1.0, nan]
    
    
        [NaN]
        [1.0]
    

Sort routines should not return a list of a different length than their input.

~~~
ww520
Elegance tends to be grinded away when the rubber meets the road.

------
alkonaut
So a divide-and-conquer algorithm on collections is more elegant functionally
than imperatively? Also: water found wet.

Haskell is elegant & Java isn't, but cherry picking examples always comes with
a risk of making your argumentation straw-man-ish.

Would be interesting to see some examples where imperative isn't so horrible,
and how Haskell compares. The in-place sort the author mentions, for example.

~~~
Silhouette
_So a divide-and-conquer algorithm on collections is more elegant functionally
than imperatively? Also: water found wet._

Even that is only true if you consider the concise and readable nature of the
Haskell code to be the most important factor in the elegance of the algorithm.

To me, the elegance of true quicksort is that its in-place nature is memory
efficient, and consequently also cache-friendly on modern hardware, resulting
in excellent real world performance.

If you consider the underlying nature of the algorithm to be more important to
its elegance than superficial presentation details, then the typical 3-line
functional implementation is clumsy by comparison, and equating the two is at
best an appeal to having a sufficiently smart compiler.

In reality, of course, both the aesthetics and the underlying behaviour
matter, so I'm not sure it's particularly helpful to promote any language as
being superior on either basis without also considering or at least
acknowledging the other.

------
ExpiredLink
The Java example builds up a straw man. Never underestimate your readers!

~~~
the_af
Why is it a straw man? It looks reasonable to me and I'm a Java programmer.

~~~
ww520
For one thing, the Haskell version is a non-inplace inefficient HelloWorld
kind of qsort. For another, the Java version is rigged to add more unnecessary
fluff.

~~~
the_af
The Haskell version isn't in-place and therefore not really quicksort, agreed,
but that's a separate (though valid) criticism. It doesn't make a "straw man"
of _the Java version_ , does it? It would be a straw man if it said "here,
look at a reasonable quicksort implementation in Java (absurd, bloated code
follows)".

The Java version doesn't really have a lot of unnecessary fluff. What, it's
not a static method and has instance variables? So what? That doesn't add a
lot of verbosity and is NOT the crux of the author's argument either.

~~~
ww520
The author was doing a section by section comparison of the two - Look!
There's no Haskell needed for the corresponding Java code! He is deliberately
showing verbosity in Java with an apple-to-orange strawman comparison. What
else is he trying to show?

The instance variable in class is an important strawman the author added to
Java. He's trying to show the need of "state" in Java, which is not needed in
a sensible Java version of qsort, as all data can be passed in parameters.

He also made the statement that the instance variable is needed for recursion
in Java (!) to "substantiate" (make up) the excuse for using instance variable
in Java.

And yes, those are called strawman.

~~~
the_af
Ah, yes, I didn't catch that he explicitly mentions state in the Java program,
as part of the comparison. That is indeed a straw man.

------
thinkpad20
Ah, the ole "quicksort in 3 lines" argument. There are a few things I take
from this.

The good:

1) The definition of the algorithm is clear. It shows "how quicksort works."

2) It's trivial to see (and prove) that the function will terminate, and
almost as trivial to prove that it will result in a sorted list. So, it is
easy to show correctness.

3) The polymorphism makes this an easily reusable function right out of the
box.

The bad:

1) That implementation is very inefficient. At a glance I think it would be
O(n^2) time. (Edit: this is misleading, because it's only O(n^2) in the same
way that quicksort is always O(n^2). It is inefficient in terms of memory
usage, though. And possibly other ways; for instance, I'm not sure how
laziness would affect this. But I don't want to be spreading FUD...)

2) The "efficient" implementation given at the bottom is just as inscrutable
as any other quicksort implementation I've seen. More so because of the
monadic code, single-letter variables (pr?) and opaque library function calls
(unsafePartition?) being made. And I'm not even sure that it would work on a
list, although since V.Vector appears to be a type class, perhaps list is an
instance of it.

3) Both the efficient and inefficient implementations are concise in large
part because of their use of library functions. This is often a good thing:
Haskell provides a great way to abstract things because of its parametric and
ad-hoc polymorphism, and allows for a lot of reusable code. But it comes at a
cost too, which is that the actual instructions you're giving to the machine
are very far removed from what the computer is doing. Who knows how much code
is _actually_ executed, how deep the rabbit hole goes, to translate those
beautiful 4 lines into actual machine instructions? With Java, it's precisely
visible what the machine is doing to execute your code. This is much less the
case with Haskell.

I suppose you could say (broadly) that functional languages excel at
expressing _what_ your program should do, while imperative languages excel at
expressing _how_ your program should do it. There are cases when you care more
about the former, and cases when you care more about the latter. The reason I
take issue with the quicksort example, is that list-sorting is a case where
you _definitely_ care more about the _how_ than the _what_.

\-------------------

EDIT, since two people called me out on it: It was an overstatement on my part
to say that it's "precisely visible" what your Java code will translate to,
but to suggest the two languages have the same degree of abstraction from the
CPU is ridiculous. The code translation from Java to bytecode is quite
straightforward, because most of the optimization occurs at runtime with JIT.
There is a reasonably direct relationship between the code you write, the
bytecode it gets translated into, and the instructions executed at runtime. At
the end of the day, Java code consists of a series of instructions for the
computer to follow, while on the other hand in Haskell, you aren't even
technically giving instructions at all -- you're just writing equations.
Compiled Haskell code is completely inscrutable.

Also, I should note that I am an enthusiastic Haskell hobbyist, and write it
on almost a daily basis. Although I might not come across it here, I am a huge
fan of the language; outside of the languages I use at work, by far the one I
use most is haskell.

~~~
ef4
> the actual instructions you're giving to the machine are very far removed
> from what the computer is doing. Who knows how much code is actually
> executed, how deep the rabbit hole goes...

The same criticism can apply to Java. But it's not a particularly good
criticism. Unless you're interested in quantum physics, you really don't want
to know what the machine is "really doing". You want to have useful
abstractions that you can rely on.

The rabbit hole goes very deep indeed, and a lot of concepts that programmers
treat as concrete are themselves abstractions that hide a lot of complexity.

The difference in level of abstraction between Java and Haskell is much
smaller than the difference from either of them to what the actual machine is
doing. In both cases you have a compiler, a language runtime, an OS & kernel,
and processor microcode in between you and "the metal". Actually Java has one
extra step, because javac emits bytecode that needs to run in the JVM, whereas
ghc emits binaries that can be run directly by the operating system.

(Your comment about O(n^2) in time is beside the point -- quicksort is always
worst case O(n^2), so it wouldn't be quicksort otherwise. All the
implementations we're talking about have that same asymptotic behavior. The
problem with the naive Haskell implementation isn't the big-O time, it's the
big-O memory, which went from O(1) to more like O(n log n).)

~~~
xxs
quicksort is never O(1) memory but O(log N). On a flip note: Java's JITs are
very mature to the point one may know what exactly the generated assembly
would be (and display it as well -XX:+PrintAssembly)

------
skybrian
After rewriting Collections.sort() just for the fun of it, the claim that "an
hour of a developer's time is a lot more expensive than an hour of a high-
performance AWS super-duper-cluster instance" isn't all that convincing. If
you're going to rewrite sort at all, you should take time to do it right, and
the Stream-based version isn't it.

~~~
xxs
The present version of Collections.sort is actually a Timsort[1] for non-
primitives.
[1]:[http://bugs.python.org/file4451/timsort.txt](http://bugs.python.org/file4451/timsort.txt)

------
elwell
Creating an entire class for the Java version is kind of a disingenuous
comparison.

~~~
the_af
Why? In Java you _must_ place your code within a class, and it's not like in
this case it adds a lot of verbosity or overhead. I don't think the author's
main argument was classes vs no classes. Java's verbosity is caused by
something else...

~~~
Rusky
You don't have to pass the arguments through the class's member variables.

~~~
the_af
That's a really minor issue, and to me it's disingenuous to imply it makes a
difference for the comparison at hand.

Would it _really_ change the argument if the Java code used a static method
and passed all variables as arguments, instead of using instance members?

~~~
ww520
Yes, it would make a big difference and make one of his central claims go away
- stateful requirement for qsort in Java.

~~~
the_af
You're right, as I replied to you elsewhere. I had missed that the author made
the "stateful" argument, and I focused on verbosity and things like i and j
instead.

------
noname123
Hi, was wondering if functional language and Java peeps can speak to the
performance Java 8's stream(...); curious if the performance of using lambda
expressions hold up against say using the old for loops and also whether they
do any caching or build internal lookup tables when you do anyMatch(...) or
something similar on a subfield of a object.

Also for any C# peeps who already have had experiences using lambda
expressions on the awesome .net platform. How was performance there?

------
_random_
No need to switch to an alien language, just opt for C#/F# for a nice middle
ground:

[http://fsharpforfunandprofit.com/posts/fvsc-
quicksort](http://fsharpforfunandprofit.com/posts/fvsc-quicksort)

------
badman_ting
_Yes, I know this is not a "true", in-place quicksort. But those are
performance considerations that I don't intend to expose here._

Beware, beware.

