1
votes

I would like to have a State Monad, where I keep a list of modes.

data State = S { 
      modes :: [Mode]
}

However, I have two requirements:

  1. Mode needs to be a parametrized with a type.
  2. Mode can be built on run time.

Here is how I tried to convince GHC:

Attempt 1, with a data type

data Mode a = Mode { 
      complF :: String -> IO [String]
      action :: State -> String -> X a
}

data State a = S { 
      modes :: [Mode a]
}

initState :: (String -> IO [String]) -> (State -> String -> X a) -> State a
initState c a = {
      modes = [buildMode c a]
}

buildMode :: (String -> IO [String]) -> (State -> String -> X a) -> Mode a
buildMode c a = {
       complF = c
     , action = a
}

Good.. however, this is not useful for me, since every mode has to be of type Mode a inside a State a. Meaning that no Mode String and Mode () can co-exist within a State.

Attempt 2:, type class

class Mode a where
       complF :: String -> IO [String] 
       action :: State -> String -> X a

data State = S {
       modes :: (Mode a) => [a]
}

But, now I don't know how to build a mode in run time given complF and action. It seems that instances of type classes must be defined at compile time.

instance Mode DefaultMode where
       complF :: .. 
       action :: ..

Is there a workaround?

1
You can create run-time instances with hackage.haskell.org/package/reflection, but it's not easy.Sjoerd Visscher

1 Answers

4
votes

If modes support a fixed API, you can use an existential type to hide their representation type. E.g.

data State = S { 
  modes :: forall a . ModeLike a => [Mode a]
}

and then implement a class ModeLike that has only those operations supported by Mode types. Your 2nd attempt is close to this.

Since type class instances are open, you add new instances without changing your code.

If you have, however, a fully dynamic system -- i.e. you cannot enumerate the types that will be possible modes - you will have to use a dynamic type as one of the instances.