{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE UndecidableInstances #-} module Glazier.Gadget 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 Control.Monad.State.Strict import Data.Semigroup -- | The Elm update function is @a -> s -> (s, c)@ -- This is isomorphic to @ReaderT a (State s) c@ -- ie, given an action "a", and a current state "s", return the new state "s" -- and any commands "c" that need to be interpreted externally (eg. download file). -- This is named Gadget instead of Update to avoid confusion with update from Data.Map newtype GadgetT a s m c = GadgetT { runGadgetT :: ReaderT a (StateT s m) c } deriving ( MonadState s , MonadReader a , Monad , Applicative , Functor , Fail.MonadFail , Alternative , MonadPlus , MonadFix , MonadIO ) makeWrapped ''GadgetT type Gadget a s = GadgetT a s Identity _GadgetT :: Iso (GadgetT a s m c) (GadgetT a' s' m' c') (a -> s -> m (c, s)) (a' -> s' -> m' (c', s')) _GadgetT = _Wrapping GadgetT . iso runReaderT ReaderT . iso (runStateT .) (StateT .) {-# INLINABLE _GadgetT #-} -- | Non polymorphic version of _Gadget _GadgetT' :: Iso' (GadgetT a s m c) (a -> s -> m (c, s)) _GadgetT' = _GadgetT {-# INLINABLE _GadgetT' #-} mkGadgetT' :: (a -> s -> m (c, s)) -> GadgetT a s m c mkGadgetT' = review _GadgetT {-# INLINABLE mkGadgetT' #-} runGadgetT' :: GadgetT a s m c -> (a -> s -> m (c, s)) runGadgetT' = view _GadgetT {-# INLINABLE runGadgetT' #-} belowGadgetT :: ((a -> s -> m (c, s)) -> a' -> s' -> m' (c', s')) -> GadgetT a s m c -> GadgetT a' s' m' c' belowGadgetT f = _GadgetT %~ f {-# INLINABLE belowGadgetT #-} underGadgetT :: (ReaderT a (StateT s m) c -> ReaderT a' (StateT s' m') c') -> GadgetT a s m c -> GadgetT a' s' m' c' underGadgetT f = _Wrapping GadgetT %~ f {-# INLINABLE underGadgetT #-} overGadgetT :: (GadgetT a s m c -> GadgetT a' s' m' c') -> ReaderT a (StateT s m) c -> ReaderT a' (StateT s' m') c' overGadgetT f = _Unwrapping GadgetT %~ f {-# INLINABLE overGadgetT #-} aboveGadgetT :: (GadgetT a s m c -> GadgetT a' s' m' c') -> (a -> s -> m (c, s)) -> a' -> s' -> m' (c', s') aboveGadgetT f = from _GadgetT %~ f {-# INLINABLE aboveGadgetT #-} instance MonadTrans (GadgetT a s) where lift = GadgetT . lift . lift instance MFunctor (GadgetT a s) where hoist f (GadgetT m) = GadgetT (hoist (hoist f) m) instance (Monad m, Semigroup c) => Semigroup (GadgetT a s m c) where (GadgetT f) <> (GadgetT g) = GadgetT $ (<>) <$> f <*> g {-# INLINABLE (<>) #-} instance (Monad m, Monoid c) => Monoid (GadgetT a s m c) where mempty = GadgetT $ pure mempty {-# INLINABLE mempty #-} (GadgetT f) `mappend` (GadgetT g) = GadgetT $ mappend <$> f <*> g {-# INLINABLE mappend #-} -- | zoom can be used to modify the state inside an Gadget type instance Zoomed (GadgetT a s m) = Zoomed (ReaderT a (StateT s m)) instance Monad m => Zoom (GadgetT a s m) (GadgetT a t m) s t where zoom l = GadgetT . zoom l . runGadgetT {-# INLINABLE zoom #-} -- | magnify can be used to modify the action inside an Gadget type instance Magnified (GadgetT a s m) = Magnified (ReaderT a (StateT s m)) instance Monad m => Magnify (GadgetT a s m) (GadgetT b s m) a b where magnify l = GadgetT . magnify l . runGadgetT {-# INLINABLE magnify #-}