Hacker News new | ask | show | jobs
by mrkeen 1292 days ago
> I always feel these posts are filled with category theory jargon

The 80/20 rule really applies here. Most of the time there's only a few key type classes that people use. Pretty much just Monad, Monoid, Applicative, Functor. If you grok those, then what people say makes a lot more sense.

> Can anyone give me a practical applied way in which category theory is a benefit to your design

Monad is probably the best example, because so many different, important libraries use it. Once you know how to code against one of them, you kinda know how to code against all of them!

The following is code written in the STM monad. It handles all the locking and concurrency for me:

   transfer x from to = do
       f <- getAccountMoney from
       when (f < x) abort
       t <- getAccountMoney to
       setAccountMoney to   (t + x)
       setAccountMoney from (f - x)
This same code could be in the State monad (excluding the line with 'abort' on it). Or it could be in the ST monad. Or the IO monad. Each of those is doing radically different things under the hood, which I don't have the brainpower to understand. But because they expose a monadic interface to me, I already know how to drive them using simple, readable, high-level code.

As well as the 4 monads mentioned above, parsers are monadic, same with Maybe and Either, List. The main concurrency library 'async' is also monadic (though it just uses the IO monad rather than defining an Async monad).

Often the people writing your mainstream libraries know this stuff too. If you're calling flatMap (or thenCompose), you're calling monadic code. The writers just shirk from calling it monadic because they're worried people will suddenly not understand it if it has a scary name.

1 comments

Looks like to the hard part is ‶handl[ing] all the locking and concurrency″ with a pretty interface; that it satisfies the definition of a monad is a convenience because the `do` keyword requires it, but so would it be if it satisfied any other one, wouldn't it?
I would say it's the other way around:

1. Because the underlying concept for handling composing locks (STM, which I don't know much about, but I'm going to assume is a way to wrap operations within a transaction) is sound

2. it is easy to define the corresponding monad

3. which makes it usable in a do notation

4. producing readable and future-proof code

The step 2 is almost trivial, 1 is where the thinking is at. The do notation is nice to have, but not that important (to me at least). I have no problem writing this in CPS style when using javascript or c++:

   const transfer = (x, from, to, onSuccess, onError) => {
      return getAccountMoney(from, (f) => {
         if (f < x) { 
             return onError();
         } else { 
             return getAccountMoney(to, (t) => {
                 return setAccountMoney(to, t+x, () => {
                     return setAccountMoney(from, f-x, onSuccess, onError);
                 }, onError);
             }, onError);
         }, onError);
     }
   }
The main point is that I can add 80 edge cases to this and it will continue to compose nicely, not a single lock in sight.