1 Comment
User's avatar
⭠ Return to thread
liam's avatar

Monads are generic types that have a lawful (contract obeying) implementation into the Monad typeclass, or interface.

It's just really really abstract, the three essential methods are fmap, pure, and join (>>= is fmap then join), with laws needing to be observed, i.e, fmap, pure, and join are defined by their types and lawfulness, not any specific implementation.

As an example of how abstract it is, Proxy a is a monad, even though it's a type with only one value.

Haskellers make a big deal out of monads because they can be used to model side effects and provide control of side effects, while also allowing for monad generics (mtl monad classes, effect systems), and the basic universal IO type supports the monad interface.

You can think of it as "funky imperative programming" due to the do-notation desugaring, Reader gives me read-only state, Writer write-only state, State read-write state, etc. Lists will be executed per item in the list, and so on.

Effect system libraries allow me to limit a monad block's access to IO, like, I can have code that is allowed to write to console, but not make a network call, and so on.

The problem with monads is that they impose a performance penalty and they're often verbose, with classy generic monads having huge type signatures.

For smaller apps, you can just stick to basic IO and call it a day. Larger apps, classy monads improve maintainability more than they clunk up the code.

***

As for Haskell as a problem solving language, use let expressions and where clauses a lot, with the foo = error "todo" idiom for parts you can't figure out how to express.

The language is asking you, basically, to define your problem, and the functions are the ones that actually solve it.

Working out problems, write the problem out in your native language, and then define the terms anf subterms you use for your definition repeatedly.

Then use equational reasoning to build a more performant and readable solution.

Expand full comment