2 ms·
> 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 th
by mrkeen 4y 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.
- shakow 4y agoLooks 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?
- larve 4y agoI 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.