Code-Reviewing My First Monad Implementation :: Reasonably Polymorphic
←
August 21, 2026
code-review, haskell, monad
I’ve been wanting to get back into the habit of blogging, and thought a fun project would be to go back through my old code and review it as the programmer I am now. So let’s do that.
Eleven years ago, I was so proud to written my first monad that I typed up a big olblog post about it. This seems like a fun thing to revisit, so let’s do it.
To save you the trouble of reading that ancient-ass blog post, here’s the gist. Given a big piece of state, that has many smaller stateful subcomponents, for example, a World is full of Buffers:
data World = World<br>{ wBuffers :: [Buffer]<br>, wCurrent :: Int<br>, wMode :: Mode
data Buffer = Buffer<br>{ bFilename :: FilePath<br>, bContent :: String
my monad acted like StateT except that you can restrict the piece of the state you’re allowed to manipulate without losing the ability to look at the rest of the state.
In the blog post, this monad is called Jurisdiction, but at some point in the source code it got renamed to JailT. Cool name. Furthermore, the implementation changed rather dramatically from the blog post, so let’s chase the implementation as given. I’ll reproduce it here:
type RLens r s = Lens s s r r
newtype JailT s r m a =<br>JailT<br>{ runJailT' :: Bool<br>-> RLens r s<br>-> s<br>-> m (a, s, RLens r s, Bool) }<br>deriving (Functor, Typeable)
instance (Applicative m, Monad m) => Applicative (JailT s r m) where<br>pure x = JailT $ \v l s -> pure (x, s, l, v)<br>() = ap
instance (Applicative m, Monad m) => Monad (JailT s r m) where<br>return = pure<br>ac >>= k = JailT $ \v l s -><br>if v<br>then do<br>(x, st', l', v') runJailT' ac v l s<br>runJailT' (k x) v' l' st'<br>else return (undefined, s, l, v)
Already there’s a lot to look at. The most immediate thing that strikes me is the formatting; now I’m very much a “use two spaces for indent” kind of guy — Haskell doesn’t provide many opportunities for natural linebreaks, and our horizontal space is much more limited than our vertical space. So don’t throw away your horizontal budget on initial spacing. It’s minor, but being an artisan means caring about the minor details.
newtype JailT s r m a = JailT<br>{ runJailT'<br>:: Bool<br>-> RLens r s<br>-> s<br>-> m (a, s, RLens r s, Bool)<br>deriving (Functor, Typeable)
Much nicer.
The other exciting thing to see in the earlier snippet is that this code comes from before the Functor-Applicative-Monad transition. Which is to say that it’s from the long long ago before Applicative was a superclass on Monad. There’s some history for you.
And then there is the elephant in the room. Whatever this Bool is being passed around by runJailT', the Monad instance branches on it and returns undefined when it’s False. What the hell is that???? Digging into the MonadPlus instance provides some insight:
instance (Applicative m, Monad m) => MonadPlus (JailT s r m) where<br>mplus x y = JailT $ \v l s -> do<br>x'@(xa, xs, xl, v') runJailT' x v l s<br>if v'<br>then return x'<br>else runJailT' y v l s<br>mzero = JailT $ \v l s -> return (undefined, s, l, False)
So mzero returns undefined and some False value, presumably which is there to try to warn someone that there’s an undefined floating around.
This is a stupid design. If you’re ever in the business of needing to store data-which-might-not-be-valid and a separate tag stating whether or not it’s valid, a much better design here would be to just use Maybe a instead of (a, Bool).
In fact, perhaps I realized this later on, because runJailT does exactly this logic:
runJailT :: (Functor m)<br>=> JailT s s m a<br>-> s<br>-> m (Maybe (a, s))<br>runJailT ac = fmap (\(a, s, _, v) -><br>if v then Just (a, s) else Nothing)<br>. runJailT' ac True id
Let’s roll back a bit. The pivotal primitive of JailT is the aptly-named jail:
jail :: (Monad m)<br>=> RLens r' r<br>-> JailT s r' m a<br>-> JailT s r m a<br>jail l' m = JailT $ \v l s -> do<br>(a, s', _, v') runJailT' m v (l . l') s<br>return (a, s', l, v')
Pretty reasonable definition here. I’m not sure what the idea behind RLens being a flipped version of Lens' was for. The only odd choice here is that in the big tuple returned in jail, l gets passed along unchanged. We can tell from looking at it that l here is the current lens we’re looking at. But jail is the only primitive that changes the lens, and it takes a JailT as an argument. Which is all to say that there isn’t anything actually stateful going on with the RLens. So without looking any further, I’d bet dollars to donuts that this l getting returned is purely vestigial and serves absolutely no purpose. Which in turn means we should be able to chop it out of the definition of JailT.
Speaking of stupid anti-patterns, here’s another one:
into :: RLens a (Maybe a)<br>into = lens fromJust (const Just)
jailMaybe :: (Applicative m, Monad m)<br>=> RLens (Maybe r') r<br>-> a<br>-> JailT s r' m a<br>-> JailT s r m a<br>jailMaybe l d m =<br>(gets $ view l) >>= \case<br>Just _ -> jail (l . into) m<br>Nothing -> return d
This is horrendous for a few...