Here’s an early example from the article:
import Control.Monad.State
type Interact a = StateT Int IO a
repl :: Interact ()
repl = forever $ do
val <- liftIO readLn
state <- get
liftIO $ putStr "Total: "
liftIO $ print (val + state)
put val
main = runStateT repl 0
Here’s something with similar behaviour written in Python, an imperative language with a reputation for being easy to read: total = 0
while True:
total += input()
print "Total:", total
The difference is striking. The latter is obviously much more concise. Ironically, it also seems clearer about how the state is used, having the initialization, reading and writing of that state all kept together. It’s worth considering that the stateful logic is relatively kind here, too, because there’s only a single value that needs to be maintained via StateT in the Haskell version.The Python code has the added advantage of actually being correct, assuming that the intended behaviour really is to add up the total of all entered numbers over time as the output suggests.
I think this example is instructive in terms of advancing industrial practice rather than the theoretical state of the art. Haskell demonstrates a lot of nice features that mainstream industrial languages lack today, but as soon as any sort of state or effects enter the picture, it becomes absurdly complicated to describe even simple systems. I would love to have future languages incorporate some of the safety that Haskell offers, but I think to catch on with working programmers they will have to hide away a lot of the state/effect details where they aren’t needed, just as tools like type inference have hidden away their own form of boilerplate. It’s not just about not needing to be an expert on category theory, it’s about not having to be aware of it at all.