purescripthalogen

purescript-halogen: modify state by effectful computation


I want to modify my state with a function that depends on the old state, but also introduces some randomness. My function f looks like this:

f :: State -> Eff (random :: RANDOM) State

I guess my state should be pure, and I had no idea how to get rid off Eff, other than using unsafePerformEff, so I did this:

eval :: Query ~> H.ComponentDSL State Query g
eval (Tick next) = do
  H.modify (unsafePerformEff <<< f)
  pure next

This works, but there has to be another, more safe way. I already added the random effect to my main function:

main :: Eff (H.HalogenEffects (random :: RANDOM)) Unit

But how should eval look like? Maybe modify does not work here, and there is another way to update state?

Purescript Halogen, side effect (random number) does not work for me, since f depends on the old state.


Solution

  • modify itself doesn't let you perform effectful updates, but yes, you can use get and then modify (or set) afterwards to do so. Adapted from the other example with random:

    module Main where
    
    import Prelude
    import Control.Monad.Aff (Aff)
    import Control.Monad.Eff (Eff)
    import Control.Monad.Eff.Random (randomInt, RANDOM)
    import Halogen as H
    import Halogen.HTML.Events.Indexed as HE
    import Halogen.HTML.Indexed as HH
    import Halogen.Util (runHalogenAff, awaitBody)
    
    type State = { n :: Int }
    
    initialState :: State
    initialState = { n: 3 }
    
    data Query a = NewRandom a
    
    ui :: forall eff. H.Component { n :: Int } Query (Aff (random :: RANDOM | eff))
    ui =
      H.component { render, eval }
        where
        render :: State -> H.ComponentHTML Query
        render state =
            HH.button
                [ HE.onClick $ HE.input_ NewRandom ]
                [ HH.text $ show state.n ]
    
    
        eval :: Query ~> H.ComponentDSL State Query (Aff (random :: RANDOM | eff))
        eval (NewRandom next) = do
          state <- H.get
          nextState <- H.fromEff (nextRandom state)
          H.set nextState
          pure next
    
        nextRandom :: State -> Eff (random :: RANDOM | eff) State
        nextRandom { n } = do
          nextN <- randomInt (n + 1) (n + 10)
          pure { n: nextN }
    
    main :: forall eff. Eff (H.HalogenEffects (random :: RANDOM | eff)) Unit
    main =
        runHalogenAff do
        body <- awaitBody
        H.runUI ui initialState body
    

    What could be leading to the type error is this type signature though:

    f :: State -> Eff (random :: RANDOM) State
    

    The effect row is closed, meaning it will not unify with any other row, you probably want this instead:

    f :: forall eff. State -> Eff (random :: RANDOM | eff) State