Interpret your monads! - Concrete Monads vs Monad classes
Concrete monads or monad classes? I’ll tell you why it often makes sense to jump around between these options, and where this comes from.
Monads are the central notion to encode a program with effects in Haskell. Nowadays, we often combine several monad transformers to create the desired effect stack, relying on mmorph to juggle effects as required. But in fact, monad transformers and monad morphisms are just as old as monads in functional programming, an idea first published in 1989.
We can still benefit from that old work. The core of this talk is to discuss monads which are initial objects in the sense of category theory. Practically, this means that such monads can be interpreted in a class of other monads. Perhaps the most well-known example of such initial monads is Either e, which can be interpreted in any monad that instantiates MonadError e, through the use of liftEither.
This general principle of monad interpretation is exactly what allows us to jump around between concrete monads and monad classes. Monad interpretation can sometimes give a free performance boost, and can be used to easily write Haskell Arrows code without Arrow syntax.
Functional programming community
schedule Submitted 4 months ago
People who liked this proposal, also liked:
Evie Ciobanu - Writing Great Tests With HaskellEvie CiobanuSenior Haskell EngineerHasura
schedule 4 months agoSold Out!
Program correctness has always been an important topic in computer science, and is only showing growth as software becomes more complex. Statically typed functional programming provides compiler guidance and guarantees. Advanced typing features can provide additional ways to restrict and formally prove properties within the constraints of the original program.
This has even (half-seriously) popularized remarks about how it's enough to get a program to compile to be sure it is correct. While there is some degree of truth to this (Haskell programs can escape some classes of bugs by merely enabling all warnings and having a reasonable coding standard in place), this is obviously not true for all but most trivial software.
Formal verification can help whenever that is not possible, but it is still impractical for all but small and critical systems. We still rely on writing automated unit, property, and integration tests to ensure our programs behave in the way we expect them.
At Hasura, we run these tests automatically in continuous integration (CI) services, and they often take up the majority of CI time. We dedicate a significant amount of time writing and maintaining these tests.
What is a good test? How do we write good tests? How do we organize them? What's important to consider when writing tests?
I describe common patterns and recommended style guides for writing tests that optimize for ease of reading and understanding rather than writing or modifying. It should be apparent what it is that we're testing, what are the assumptions of the test, what is being set up or arranged before the test, as well as what it is we are testing and what is the expected response. We should strive to maximize the domain information and minimize accidental complexity, such as particularities of the testing framework or programming language we're using.
In a language as expressive as Haskell, we should be able to write unit and integration tests that are understandable and modifiable by domain experts (such as power users or product owners), not only Haskell developers.
This can generally be achieved by focusing on getting the domain-specific data together in a record, thus making the test-relevant information easily readable and understandable.