Search code examples
design-patternshaskellmonadsstate-monad

How can I parameterise my Haskell functions?


I hope my terminology is correct here - if not, feel free to edit anything.

I'm using Haskell as a helper-language whilst writing a paper on combinatorial game theory - i.e., all of my functions just juggle numbers and help me to come to a solution for the game I'm studying.

I wrote all of the functions for a concrete, integral 'board size' (think chequerboard, 5x5 etc). I wanted to expand to study boards of any size, so modified all the relevant functions by including an Integer parameter. e.g.,

type Size = Int

squares :: Size -> [Square]
squares size = [ (x,y) | x <- [1..size],  
                         y <- [1..size]]

This, however, has started to get messy. Functions which normally I would consider size-independent have to be supplied with a Size whenever they access a function which requires a Size.

This very quickly leads to lines like this:

reaching size = (losing size) \\\ (setA size)

takeUDmir _  []       = []
takeUDmir size (x:xs) = [x] ++ takeUDmir size (xs \\\ mirUD size x)

rotate size s = concatMap (mirUD size) (mirLR size s)

(Note that the content of the functions really doesn't matter, I'm just trying to show how out-of-hand it has become.)

I'm pretty confident using Haskell, and with functional programming in general, but I've got no idea how I could go about removing all of these size references, and simply relying on something else which sets the size for each function which requires its use.

I think I'm probably looking for a monad, but I've got no idea.


Solution

  • This is a perfect time to pull out the Reader monad—it abstracts the notion of some globally available, read-only configuration data.

    data Config = Config { size :: Int, ... }
    
    type MyMonad = Reader Config
    
    fun :: MyMonad Result
    fun = funNeedingTheSize <$> asks size <*> pure arg1 <*> pure arg2
    
    runMyMonad :: Config -> MyMonad a -> a
    runMyMonad = flip runReader