Accessing global environment outside of Halogen component?

Alright, after some research and digging I’ve figured it out! Here is the demo: https://try.purescript.org/?gist=0270c9fd41d1d9643387f15fc0039ca1

main = HA.runHalogenAff do
  body <- HA.awaitBody
  clickCountRef <- H.liftEffect $ Ref.new 0

  let
    environment :: Env ExternalState
    environment = { state: clickCountRef }
    
    rootComponent :: forall q. H.Component HH.HTML q (Input ExternalState) Output Aff
    rootComponent = H.hoist (runAppM environment) component

  io <- runUI rootComponent initExternal body
  io.subscribe $ CR.consumer $ runAppM environment <<< subscribe
  pure unit
  
  where
    subscribe :: forall r. Output -> AppM ExternalState (Maybe r)
    subscribe = case _ of
      Changed -> do
        state <- getState
        liftEffect $ logShow state
        pure Nothing

The two things that tie it all together are:

  • Since runUI only accepts and returns an Aff, the Consumer that io.subscribe takes has to be Consumer i Aff a. This meant I had to transform AppM to Aff before calling io.subscribe, and the only place I could do that was between subscribe and consumer.

  • Because Ref uses a mutable JavaScript object, I can call runAppM twice and they will still share state, as long as both calls get the same environment. I’ll admit I feel a little weird using mutable state, but it seems to be the recommended strategy in Real World Halogen.

These resources were extremely helpful in deepening my understanding of how coroutines, Halogen, natural transformations, Aff, and Refs all fit together:

The Halogen Guide and Real World Halogen were also great references.

3 Likes