haskellghcjsreflex

How to make MonadError work with ghcjs/reflex


I'm struggling to compile the following program:

import           Data.Maybe
import qualified Data.Text       as T
import qualified Data.Text.IO    as T
import           Data.URLEncoded
import           Reflex.Dom

url :: T.Text
url = T.pack "parm1=one&parm2=two"

main = do
  mainWidget body

body :: MonadWidget t m => m ()
body  = el (T.pack "div") $ do
  -- let t = (T.pack "this program works if you replace the line below with this")
  t <- fmap (T.pack . fromMaybe "" . Data.URLEncoded.lookup "parm2") (importString (T.unpack url))
  text t

however this similar version works with vanilla ghc

import           Data.Maybe
import qualified Data.Text       as T
import qualified Data.Text.IO    as T
import           Data.URLEncoded

url :: T.Text
url = T.pack "parm1=one&parm2=two"

main = do
  body

body  = do
  t <- fmap (T.pack . fromMaybe "" . Data.URLEncoded.lookup "parm2") (importString (T.unpack url))
  T.putStrLn t

The compiler says something is ambiguous and I'm not really sure how to implement these to work.

  The type variable ‘e0’ is ambiguous
  Relevant bindings include body :: m () (bound at reflex.hs:14:1)
  These potential instances exist:
    instance [safe] Control.Monad.Error.Class.MonadError e (Either e)
      -- Defined in ‘Control.Monad.Error.Class’
    ...plus 13 instances involving out-of-scope types
      instance [safe] Control.Monad.Error.Class.MonadError
                        GHC.IO.Exception.IOException IO
        -- Defined in ‘Control.Monad.Error.Class’
      instance [safe] (Monad m, Control.Monad.Trans.Error.Error e) =>
                      Control.Monad.Error.Class.MonadError
                        e (Control.Monad.Trans.Error.ErrorT e m)
        -- Defined in ‘Control.Monad.Error.Class’

FYI: I haven't fully grasped Monads yet and easily get scared with these errors. Help!


Solution

  • In the ghc version, importString is operating in the context of an IO monad do statement . importString is capable of returning a value in the IO monad so the compiler is happy

    In the ghcjs version, importString is operating in the context of an m monad do statement (m is specified in the declaration of body ). importString has no way to return a value in the m monad so the compiler complains.

    You can get around this by using liftIO to change an IO monad value to a m monad value. Here's your code with this change and a few other changes that I made to help myself understand the code.

    import           Data.Maybe
    import qualified Data.Text       as T
    import           Data.URLEncoded as DU
    import           Reflex.Dom
    import           Control.Monad.Trans as CMT 
    
    url :: T.Text
    url = T.pack "parm1=one&parm2=two"
    
    main = do
      mainWidget body
    
    body :: MonadWidget t m => m ()
    body  = el (T.pack "div") $ do
      let istr = CMT.liftIO $ DU.importString (T.unpack url)
      t <- fmap (T.pack . fromMaybe "" . DU.lookup "parm2") istr
      text t