News for hackers. Stuff that matters.
Re:Programming with Effects by tel on Sunday November 24, 2019 @01:06PM (#21622124)
I'd argue this is incredibly common if you write a codebase without a specific, concrete effect stack. In Haskell, the mtl library offers constraints on types mandating that they support at least some effect. These compose together and then are executed by a custom effect "stack" that inhabits all of the constraints.

So, you might have a piece of code which can fail at using some User properties to compute a score of some sort

    computeScore :: MonadError Error m => User -> m Score
and also a piece of code that pulls a given user from a database

    getUser :: MonadDatabase m => UserID -> m User
and we can compose them

    getAndComputeScore :: (MonadError Error m, MonadDatabase m) => UserID -> m Score
    getAndComputeScore id = do 
      user <- getUser 
      computeScore user
In some sense, each of these three functions operates in a different (abstract) effect stack and the calls to getUser and computeScore were transparently "upgraded" to a more comprehensive effect stack when they were called in getAndComputeScore.

This is the basis for "effect tracking" where each piece of code is written to the minimal, necessary effect specification. Their ability to compose and operate within other compatible (abstract) effect stacks is used regularly to compose small programs into bigger ones. Then, finally, you just make a big type

    data ConcreteEffects a = ConcreteEffects ...

    instance MonadError Error ConcreteEffects where ...
    instance MonadDatabase ConcreteEffects where ...
which instantiates all of your abstract effects concretely. You might even make two, one for testing

    data ConcreteTestEffects a = ConcreteTestEffects ...

    instance MonadError Error ConcreteTestEffects where ...
    instance MonadDatabase ConcreteTestEffects where ...
These will look like compositions of the basic effects from this article. For instance, if you imagine a type of DatabaseHandles, you might have

    data ConcreteEffects a = ConcreteEffects (DatabaseHandle -> Either Error a)
which you can stare at and recognize to be Either and Reader combined together.

There are no comments on this comment yet.