1

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.

Community
  • 1
  • 1
stholzm
  • 3,395
  • 19
  • 31

1 Answers1

1

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
gb.
  • 4,629
  • 1
  • 20
  • 19
  • I used a closed row of effects because I know my computation only involves randomness and I wanted to forbid any other effects. But apparently this means that I cannot include a closed row in an "open" row of effects? I was also required to explicitly annotate `(Aff (random :: RANDOM | eff))` instead of just any `g`. Thanks, this made it compile! – stholzm Oct 26 '16 at 04:57
  • 1
    That's right - if you used a closed row then you're stuck with it for the entire program, so it's kinda only useful at the top level. Using an open row only lets the annotated expression use the effects you mention in the type, but being open allows it to unify with other effectful expressions later, accomodating more effects. And yep, `g` determines what type should be used to handle "non-component effects" in Halogen, so when left unspecified it doesn't admit any effectful operations. – gb. Oct 26 '16 at 10:09