Looking at your local example, I’m actually confused about what it is supposed to do. The local
I know allows you to locally alter the value that is read by the reader, which is nicely captured by its usual type signature local :: MonadReader r m => (r -> r) -> m a -> m a
, but in your case you don’t seem to alter anything.
I think local
should be good enough to show the workings of higher order effects. MonadReader
in mtl
does have local
as a higher order operation that you can overload with your own instance. For example, you can choose not to apply the function:
newtype R r a = R (r -> a) deriving Functor deriving (Applicative, Monad) via ...
instance MonadReader r (R r a) where
...
local _ x = x
This handler changes the meaning of all uses of local
in all existing programs written in the overloaded mtl
style.
Can you define a local
operation that allows you to delay the choice of whether or not to apply the context-modifying function to the time when the user applies the handler?
(This case is indeed a bit too simple, because I guess you could simply add a boolean in the record to indicate whether or not to apply the function, but what if someone comes around later and wants some different kind of behavior, like applying the function n
times? You could change the boolean to a natural number, but does that capture all possible behaviors? I hope you can agree that a solution like that won’t work for all higher order operations.)