

From zero to cooperative threads in 33 lines of Haskell - lelf
http://www.haskellforall.com/2013/06/from-zero-to-cooperative-threads-in-33.html

======
tikhonj
I think this is a perfect example of one of the main ideas behind lazy
functional programming: we can reify computations as simple data structures.

This thread system? It's just a large lazy tree. This lets us implement all
the logic in a straight-forward way, just by operating on the data type; since
the language is lazy, everything ends up being evaluated at the right time.

To me, this was one of the bigger revelations with Haskell. Stop thinking
about lists as _lists_ , and consider them more like loops. Just loops that
you can pattern match against and manipulate in a truly first-class way. Then
extend this to computations of any other "shape" by thinking about other lazy
structures.

Free monads are in some ways the ultimate example of this in action: you can
use do-notation--which clearly _looks_ like a computation--to just produce a
data structure. Then you're free to consume it however you like. Monads
usually represent computations, perhaps with effects, but here we have an
extremely general one that is just an algebraic data type!

This works because we can decouple defining the data structure from evaluating
it. We can build up our tree with a sequence of calls to fork and so on, in
the order of our program flow. Then we can actually evaluate it in the order
of our scheduler! This is one of the core ideas behind laziness.

~~~
tetha
Reasoning similar to this enabled me to write a rather powerful test recently.

We had a queue that was behaving strangely and we didn't reallz know how to
tackle this. Thus I just went ahead and realized: If we take enqueue and
dequeue as parenthesis open and parenthesis close, then a balanced string of
opening and closing parenthesis will leave the queue empty. And furthermore,
if the queue is thread safe, I can execute multiple such execution sequences
in parallel and the queue will still be empty in the end and no execution
sequence will be infinitely blocked. The threads might steal some elements
back and forth, but no thread will ever starve, because for every stolen
dequeue, and enqueue will be orphaned and thus, stolen in retaliation.

From there, I could just write myself an execution sequence generator based on
the CFG for balanced parenthesis trees, execute a number of these things in
parallel and check if the queue was empty in the end. This test failed and it
appears to be highly sensitive and precise.

------
boothead
There are some amazing things you can do with free monads, although I must
confess to not having my head completely wrapped round them yet:

There are some great links in this SO answer:

[http://stackoverflow.com/questions/6941904/recursion-
schemes...](http://stackoverflow.com/questions/6941904/recursion-schemes-for-
dummies)

Especially Tim William's talk here:

[https://github.com/willtim/recursion-
schemes/raw/master/slid...](https://github.com/willtim/recursion-
schemes/raw/master/slides-final.pdf)

~~~
MichaelAza
"I must confess to not having my head completely wrapped round them yet"

My thoughts exactly. That's the thing about Haskell. I love the elegance and
enjoy the mathematics but it seems it takes me months at a time before
something clicks. One reason why I haven't used it for any "real" project.

~~~
Martijn
You don't really need to understand all these things in order to build "real"
things with it. Just build something without caring too much whether an
experienced Haskell programmer would find it elegant enough.

Most of the "clicks" I've had is when I'd already built something in a naive
way (that worked perfectly) and someone showed me an alternative, more elegant
way in which it could be written.

------
dons
See also Koen Claessen's "Poor Man's Concurrency Monad",
[http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.39.8...](http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.39.8039)

------
tux1968
The article starts off with the English problem description, but it seems very
clear that wasn't the logical starting point. It was framed in Haskell first
and then worked back to the English.

There's nothing wrong with that. But I think it's a bit disingenuous for the
author to say he started with the English first. Who would describe
cooperative threads that way without having a deep understanding of how they
were about to implement them in Haskell?

------
blueblob
Is it just me, or does it seem like using fork to implement thread is kind of
overkill. The point of fork is that you want the context of the calling
process (and the required overhead) and implementing threads on top of fork
seems to defeat the purpose.

Note: I have not programmed in Haskell and am unfamiliar with it's fork
function.

~~~
kenko
That fork function was defined in the library.

------
jokoon
33 lines of haskell is like 2000 lines of C

~~~
qb45
At my university OS course we once had to implement a usermode threading
library in C. Not cooperative, but preemptive (signals), with all the required
locking, signal masking, atomic_t's and so on.

Mine was about 300 lines of C.

~~~
stouset
Georgia Tech?

