Skip to content

Instantly share code, notes, and snippets.

@graninas
Last active April 25, 2024 20:49
Show Gist options
  • Save graninas/1b7961ccaedf7b5cb92417a1599fdc99 to your computer and use it in GitHub Desktop.
Save graninas/1b7961ccaedf7b5cb92417a1599fdc99 to your computer and use it in GitHub Desktop.
Haskell Approaches Comparison Table

An Opinionated Comparison of Software Design Approaches in Haskell

Raw IO Free Monads; Church Encoded Free Monads Final Tagless / mtl Effect Systems ReaderT Service Handle
Core Principle Bare IO monad, imperative code Interpretable, instrospectable, custom pure monads Effects are explicit in BL; a list of constraints Effects are explicit in BL; either a list of constraints, or a list of types Interface to subsystems is described as a control structure and placed to ReaderT environment A Handle structure as an interface to subsystems
- Interfaces None ADTs Type classes; Type classes + type families Different; ADTs; type classes; type families Monadic methods placed into ReaderT env (usually IO methods, but not necessary) Monadic methods placed into a structure (usually IO methods, but not necessary)
- Business Logic Impure monadic Pure monadic Impure monadic; effects are constraints Depends; pure or impure monadic BL in a custom monadic stack with ReaderT Any kind of BL; service handle can be passed either as a parameter or as a ReaderT env
- Implementation Mixed with BL Interpreters Type class instances Different; interpreters; type class instances; type family instances Implementation is filling the control structure Implementation is filling the Handle structure
Separation of Concerns None 5/5 3/5 Depends; 3-5/5 3/5 3/5
Layering - Good, essential, unavoidable Weak; Implementation details tend to leak Depends; 3-5/5; Implementation details tend to leak; Lists of effects prevent dividing a BL into own layers 3/5 3/5
Mechanisms Simplicity 3/5 5/5 1-4/5 1/5 5/5 5/5
Complexity Reduction 1/5 5/5 3/5 2/5 3/5 3/5
Robustness 2/5; No restrictions on effects 5/5 4-5/5; Sometimes IO is allowed 5/5 3/5; No restriction on effects 3/5; No restriction on effects
Maintainability 1/5 5/5 3/5 2/5 3/5; Details are in the BL 3/5; Details are in the BL
Testability 1/5 5/5 2/5 Depends; 3-5/5 Depends; 3-4/5 Depends; 3-4/5
Extensibility 3/5 4/5 4/5 3/5 3/5
- Effects 5/5 2/5 5/5 3/5 3/5 3/5
- Domain Features 4/5 3/5 3/5 5/5 5/5
Rare design cases Will be a mess Approachable, relatively simple Hard, mind blowing, complicated Hard, mind blowing, sometimes extremely complicated 2-3/5; Approachable but often complicated 2-3/5; Approachable but often complicated
Expressiveness Not limited; not convenient Very expressive; Different semantics and syntax is possible; Very limited syntax; limited semantics Very limited syntax; limited semantics 4/5; Limited syntax; limited semantics 4/5; Limited syntax; limited semantics
Boilerplate 3/5 3/5 3/5 3/5 3/5
- Interfaces None 3/5 5/5 Depends; 3-5/5 3/5 3/5
- Business Logic 1/5 5/5 2/5 2/5 4/5 2/5
- Implementation 1/5 4/5 4/5 3/5 4/5 4/5
Error Handling When you're lucky Simple; Error domains Complicated Depends; usually complicated Approachable Approachable
Problems & Limitations Bare IO; Lazy IO; Bad testability; Bad separation of concerns; Bad in general FM: O(n^2) monadic binding; No exceptions in BL All layers are mixed; IO is not separated; No real separation of concerns; Bad testability; Very complicated error handling; Implementation details tend to leak into BL; On rare special cases, design becomes very hard; Too many special hacks for incorporating of some external libraries Explicit type lists of effects are really hard to maintain; A lot of difficult high-level concepts involved; Rigidness and inconvenience to use; Overall overengineering; Often a bad testability; Very complicated error handling; Implementation details tend to leak into BL; On rare special cases, design becomes very hard; Too many special hacks for incorporating of some external libraries All the layers are mixed (usually); Effects are not limited; Too hard to hide implementation details; Hard to maintain; Rare design cases can be hard to implement; All the layers are mixed (usually); Too hard to hide implementation details; Hard to maintain; Rare design cases can be hard to implement;
Performance 4~5/5 FM: 2/5; CEFM: 5/5 5/5 Depends; 4-5/5 5/5 5/5
Docs & Showcases
Market Share
Overall Bad approach; suitable for small apps; Not suitable for big real-world apps Very powerful. Best testability. Best expressiveness. Best complexity reduction. Best layering. Introspection is very useful. An approach for lazy developer. Doesn't satisfy the requirements. Suitable when there is no time for design. Not really suitable for big codebases. An approach for those who wants to control everything in the BL. Lists of effects prevent dividing a BL into own layers. Relatively simple. Can be used for small and middle apps. Code tends to become polluted by implementation details. No explicit guarantees Relatively simple.Can be used for small and middle apps.Code tends to become polluted by implementation details.No explicit guarantees
@alaendle
Copy link

@graninas, first 👍 and ❤️ .

Thank you so much for theses detailed descriptions.

So to really tell you what I think I first have to gain some more experience - especially with the free monad stuff. As a Haskell beginner, I only have experimented final tagless, readerT, raw IO and now effect systems.

Maybe its because I'm having a OOP background - but the effect systems stuffs seemed very compelling to me (at least at a first glance); I think because of its principal simplicity - when you consider the interpreters as a natural transformation (step) from your DSL to the actual program. I also think ReaderT just reminded me to simple dependency injection and therefore just felt wrong (because it couldn't really hide the "lower aspects" - and needs lot of boilerplate code). Also the limited (read: hard) composability of mtl stuff doesn't make it as a concept useful to me.

So maybe I just have to try to realize/refactor my project using Free Monads - to really get a feeling for that approach. But first I have to read trough the referenced posts and watch some videos, so that I become aware of what I am getting myself into.

I will post an update once I made some progress (in whatever direction). And please don't forget that these are questions and feedback from a beginner - so please perceive my statements against my limited Haskell/FP experience.

And once again many thanks for your effort to answer my questions in such detail.

@graninas
Copy link
Author

graninas commented Aug 11, 2020

@alaendle, sure, you're welcome!

Just to note: some effect systems have Free monads under the hood. The very fact of this gives you the interpretation possibility. All my Free monadic solutions are interpretable in the same sense (yes, the natural transformation). The difference is how we track the effects. The effect systems ask you to compose effects as type level list of types, but I don't like this very much. For me it goes out of control quickly. My approach is to nest the Free monadic languages one into another.

I find your experience with ReaderT and mtl very interesting.

And please don't forget that these are questions and feedback from a beginner

Sure. I think many of my materials are pretty much advanced though. Don't hesitate to ask questions :)

@alaendle
Copy link

alaendle commented Feb 1, 2024

@graninas, just wanted to let you know that I just pre-ordered the second edition of your book to continue my journey to sound haskell application architecture 😄

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment