functional-programmingpurescriptpurescript-halogen

How to use logShow inside the handleAction function in Halogen 5


As the title says, I am trying to use logShow inside of my handleAction function. I imported the Effect.Console (logShow) and tried to use it like this, everytime a button is clicked:

handleAction ∷ forall o m. Action → H.HalogenM State Action () o m Unit
handleAction = case _ of
  Update -> do
    logShow "Hello"
    H.modify_ \st -> st { field3 = st.field3 + 1 }

But I only get the following Error, and I don't understand very much, as I am very new to purescript and functional programming at all.

Could not match type

    Effect

  with type

    HalogenM
      { field1 :: Int
      , field2 :: Int
      , field3 :: Int
      }
      Action
      ()
      o0
      m1

while trying to match type Effect t2
  with type HalogenM
              { field1 :: Int
              , field2 :: Int
              , field3 :: Int
              }
              Action
              ()
              o0
              m1
              Unit
while checking that expression (discard (logShow "Hello")) (\$__unused -> modify_ (\st -> ... ))
  has type HalogenM
             { field1 :: Int
             , field2 :: Int
             , field3 :: Int
             }
             Action
             ()
             o0
             m1
             Unit
in value declaration handleAction

where m1 is a rigid type variable
        bound at (line 77, column 16 - line 80, column 51)
      o0 is a rigid type variable
        bound at (line 77, column 16 - line 80, column 51)
      t2 is an unknown type
PureScript(TypesDoNotUnify)

I am glad about any clue.


Solution

  • I'm not an expert in PS but I'll try my best to answer it. So please correct me if I'm wrong:

    logShow type signature is MonadEffect m => Show a => a -> m Unit, means that the caller of this function should have an instance of or be constrained by MonadEffect.

    In your case, handleAction has a type signature of forall o m. Action → H.HalogenM State Action () o m Unit and you call logShow inside it. Your m here doesn't describe any particular monad while as we knew already, the caller of logShow should be constrained by MonadEffect.


    There are a couple of ways to solve this:

    1. You can add MonadEffect constraint to your handleAction type signature like so

      forall o m. (MonadEffect m) => Action → H.HalogenM State Action () o m Unit
      

      This should work since HalogenM has instance of MonadEffect as long as your m also has MonadEffect instance. See here

    2. Change your m to Aff as Aff has instance of MonadEffect. IIRC, Halogen requires your m to be or has an instance of Aff

    3. Or you could use your own monad stack that has an instance of Aff as you can see in Thomas' great repository here

    EDIT: The explanation above assumes you import logShow from Effect.Class.Console

    But if you're using logShow from Effect.Console which has type signature Show a => a -> Effect Unit, then we need some function that converts the Effect monad to your m (this function should have type sig Effect a -> m a). And liftEffect is exactly what you're looking for.

    handleAction :: forall o m. (MonadEffect m) => Action → H.HalogenM State Action () o m Unit
    handleAction _ = do
      liftEffect $ logShow "something"
      ...
    

    Hope this helps :)