Monad Transformer for Halogen Components

276 views Asked by At

I'm trying to figure out in which way I can use a transformer on the monad that a Halogen component contains.

I'd like to extend the intro example by a ReaderT that carries a Config record which in this case could be used to make the strings configurable, but I'm a lost when it comes to putting it all together.

Let's say we define our Config like this:

-- | Global configuration
newtype Config = Config { toggleText :: String
                        , onText :: String
                        , offText :: String
                        }

Our ui function would then turn from

forall m eff. (Monad m) => Component m Input Input

to

forall m (Monad m) => Component (ReaderT Config m) Input Input.

To evaluate our main function, we'd then use hoistComponent to turn it back into its previous form:

main = do
  let config = Config { toggleText: "Toggle Button"
                      , onText: "On"
                      , offText: "Off"
                      }
  Tuple node _ <- runUI $ hoistComponent (runReaderT config) ui
  appendToBody node

I'm not sure if this even makes sense so far, but pretending that it does, the next step is where I'm struggling. In an ideal world, my ui function would allow me to do something like this:

ui :: forall m eff. (Monad m) => Component (ReaderT Config m) Input Input
ui = render <$> stateful (State { on: false }) update
  where
  render :: State -> H.HTML (ReaderT Config m Input)
  render (State s) = do
    (Config conf) <- ask
    return $ H.div_ [ H.h1_ [ H.text conf.toggleText ]
                    , H.button [ A.onClick (A.input_ ToggleState) ]
                               [ H.text (if s.on then conf.onText else conf.offText) ]
                    ]

  update :: State -> Input -> State
  update (State s) ToggleState = State { on: not s.on }

But I end up with a long chain of unification errors and don't know where to start. Obviously, the inner use of ask cannot work like this, because I'd need to lift it into the HTML context, but I'm not even sure if that's possible.

It would be great if someone could guide my through the types here and tell me if that general approach is sensible. A full (non-compiling) example is on GitHub. I18n should only serve as a simple example use of the Reader here.

1

There are 1 answers

3
Phil Freeman On

The monad m is the monad for event handlers. The HTML document itself would not have access to the configuration in your Reader monad.

You would use Reader if you wanted your event handlers to have access to some configuration object, and you would have to replace the use of A.input_ with an action in the Reader monad.

To do what you want to do, you probably want something more like MonadReader Config m => m (Component _ _ _), where the component itself depends on the configuration.