In the previous section, we saw how to hide the fact that we’re using a State monad to hold the state for our Supply monad.
Another important way to make code more modular involves separating its interface (what the code can do) from its implementation—how it does it.
The standard random number generator in
System.Random is known to be quite slow. If we use our
randomsIO function to provide it
with random numbers, then our
action will not perform well.
One simple and effective way that we could deal with this is to provide Supply with a better source of random numbers. Let’s set this idea aside, though, and consider an alternative approach, one that is useful in many settings. We will separate the actions we can perform with the monad from how it works using a typeclass:
-- file: ch15/SupplyClass.hs class (Monad m) => MonadSupply s m | m -> s where next :: m (Maybe s)
This typeclass defines the interface that any supply monad must implement. It bears careful inspection, since it uses several unfamiliar Haskell language extensions. We will cover each one in the sections that follow.
How should we read the snippet
MonadSupply s m in the typeclass? If we add parentheses,
an equivalent expression is
(MonadSupply s) m, which is a
little clearer. In other words, given some type variable
m that is a Monad, we can make it an
instance of the typeclass MonadSupply s. Unlike a regular
typeclass, this one has a parameter.
As this ...