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.