HeinrichApfelmus / threepenny-gui

GUI framework that uses the web browser as a display.
https://heinrichapfelmus.github.io/threepenny-gui/
Other
437 stars 77 forks source link

Added MonadUI typeclass #173

Closed jerbaroo closed 7 years ago

jerbaroo commented 7 years ago

This is related to using a custom monad stack.

I have found using a custom monad stack useful for storing Behaviors, such that one part of the application can easily emit a new value, or some other part of the application sink that value. Simply by ask ing for the necessary function.

-- |Monad the app runs in.
type FooMonad a = ReaderT Env UI a

-- | Enironment/config the app requires.
data Env = Env {
  , eBarBehavior :: Behavior (Maybe String)
  , eBarEmit     :: Handler  (Maybe String)
  ...
  }

I also use a custom monad stack for a unique ID generator.

-- |Return a unique ID.
uniqueId :: FooMonad String
uniqueId = do
  mId <- eId <$> ask
  id' <- liftIO $ MV.modifyMVar mId (\i -> return (i + 1, i))
  return $ "__unique-id-" ++ show id'

The problem with the approach in #161 is if I choose to edit my monad stack then I'll have to change all uses of lift to perhaps lift $ lift -- and using lift doesn't scale well with the amount of stacked monads. It's easier to make the custom monad stack an instance of MonadUI and use liftUI instead of lift, then if my monad stack changes in the future I will only have to edit the instance of MonadUI, instead of all the uses of lift.

HeinrichApfelmus commented 7 years ago

Thanks! I have to admit that I'm not particularly happy that this class is necessary, but it probably can't be helped. In particular, the alternative — everybody implements their own version of MonadUI — is definitely worse. Merged!

jmickelin commented 7 years ago

I'd like to mention that using liftBase with an instance for MonadBase UI UI from https://hackage.haskell.org/package/transformers-base is an alternative, and arguably cleaner, solution, and comes with instances for all the common transformers for free.