How to structure Haskell applications?

Viewed 176

I have some experience with simple (single-focus) Haskell programs but now I am trying to write bigger applications in Haskell. I am having trouble structuring the application.

Say, we have an application with three modules (conceptually) - Main, Logic, and AppIO. Main starts the program and uses functions in Logic and AppIO to do the work. Logic abstracts the app logic and we need multiple implementations of it. Which Logic implementation is used by Main is decided by user configuration. AppIO is for functions that talk to the outside world.

My Main module has a newtype App = App { unapp :: ReaderT r (ExceptT e m) a } monad transformer. Similarly, Logic and AppIO have their own monad transformer stacks. I am thinking of creating an App monad transformer with Logic and AppIO transformers at the base.

At this point I have some questions.

  1. Is this a correct way to structure Haskell applications that have multiple monadic modules?
  2. Should Logic and AppIO have their own monad transformer stacks or should they have App in their type signatures? I don't like App to be in module types as I think modules should be agnostic to each other. How will I give Logic its own state if I don't make it a transformer in itself?
  3. If Logic and AppIO have their own ExceptT monads in their transformer stacks for error handling with their own error types i.e. MonadError LogicError and MonadError IOError, how would they work with App level ExceptT? Do I need to write lift functions? If I write lift functions then how do I have internal state (for example RandomGen) in Logic that is updated on function calls? From what I understand lift will run the monad and so its state is lost?
  4. How do I write Logic so that it has multiple implementations that may be swapped with each other without having to change anything else? Should I define a class Logic? Or should it be a data Logic with its interface functions as members?
0 Answers
Related