glazier-0.11.0.0: src/Glazier/Window.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
-- | Functional version of (Elm View/Update & startApp architecture) enabling composable widgets, and a FRP-like framework.
--
-- This framework makes it easier to modularize the Elm architecture idea of View/Update:
-- based on the deprecated Elm Architecture version of Jan 2016
-- https://github.com/evancz/elm-architecture-tutorial/tree/de5682a5a8e4459aed4637533adb25e462f8a2ae
--
-- The Elm View/Update is basically as follows:
--
-- @
-- data Model = Blah....
-- data Action = DoThis | DoThat deriving Show
--
-- -- | update is fired from an event processing loop
-- update :: Action -> Model -> Model
--
-- -- | The widget from 'view' knows how to send Action to a mailbox
-- view :: Signal Address -> Model -> Html
-- @
--
-- This module uses isomorphic Window and Gadget resulting in instances can be be composed together into larger Widgets.
-- Original inspiration from https://arianvp.me/lenses-and-prisms-for-modular-clientside-apps/
--
module Glazier.Window where
import Control.Applicative
import Control.Lens
import qualified Control.Monad.Fail as Fail
import Control.Monad.Fix (MonadFix)
import Control.Monad.Morph
import Control.Monad.Reader
import Data.Semigroup
-------------------------------------------------------------------------------
-- | The Elm view function is basically @view :: model -> html@
-- This can be enhanced with monadic effects with ReaderT.
-- This is named Window instead of View to avoid confusion with view from Control.Lens
newtype WindowT s m v = WindowT
{ runWindowT :: ReaderT s m v
} deriving ( MonadReader s
, Monad
, Applicative
, Functor
, Fail.MonadFail
, Alternative
, MonadPlus
, MonadFix
, MonadIO
)
makeWrapped ''WindowT
type Window s = WindowT s Identity
_WindowT :: Iso (WindowT s m v) (WindowT s' m' v') (s -> m v) (s' -> m' v')
_WindowT = _Wrapping WindowT . iso runReaderT ReaderT
{-# INLINABLE _WindowT #-}
-- | Non polymorphic version of _Window
_WindowT' :: Iso' (WindowT s m v) (s -> m v)
_WindowT' = _WindowT
{-# INLINABLE _WindowT' #-}
mkWindowT' :: (s -> m v) -> WindowT s m v
mkWindowT' = review _WindowT
{-# INLINABLE mkWindowT' #-}
runWindowT' :: WindowT s m v -> (s -> m v)
runWindowT' = view _WindowT
{-# INLINABLE runWindowT' #-}
belowWindowT ::
((s -> m v) -> (s' -> m' v'))
-> WindowT s m v -> WindowT s' m' v'
belowWindowT f = _WindowT %~ f
{-# INLINABLE belowWindowT #-}
underWindowT
:: (ReaderT s m v -> ReaderT s' m' v')
-> WindowT s m v
-> WindowT s' m' v'
underWindowT f = _Wrapping WindowT %~ f
{-# INLINABLE underWindowT #-}
overWindowT
:: (WindowT s m v -> WindowT s' m' v')
-> ReaderT s m v
-> ReaderT s' m' v'
overWindowT f = _Unwrapping WindowT %~ f
{-# INLINABLE overWindowT #-}
aboveWindowT ::
(WindowT s m v -> WindowT s' m' v')
-> (s -> m v) -> (s' -> m' v')
aboveWindowT f = from _WindowT %~ f
{-# INLINABLE aboveWindowT #-}
instance MonadTrans (WindowT s) where
lift = WindowT . lift
instance MFunctor (WindowT s) where
hoist f (WindowT m) = WindowT (hoist f m)
instance (Applicative m, Semigroup v) => Semigroup (WindowT s m v) where
(WindowT f) <> (WindowT g) = WindowT $ (<>) <$> f <*> g
{-# INLINABLE (<>) #-}
instance (Applicative m, Monoid v) => Monoid (WindowT s m v) where
mempty = WindowT $ pure mempty
{-# INLINABLE mempty #-}
(WindowT f) `mappend` (WindowT g) = WindowT $ mappend <$> f <*> g
{-# INLINABLE mappend #-}
-- | magnify can be used to modify the action inside an Gadget
type instance Magnified (WindowT s m) = Magnified (ReaderT s m)
instance Monad m => Magnify (WindowT s m) (WindowT t m) s t where
magnify l = WindowT . magnify l . runWindowT
{-# INLINABLE magnify #-}