packages feed

ytl-0.1.0.0: src/Yesod/Trans/Class/Reader.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

module Yesod.Trans.Class.Reader
  ( ReaderSite (..)
  , runReaderSite

  , SiteReader (..)
  ) where

import Yesod.Site.Class
import Yesod.Trans.Class

import Data.Copointed
import Yesod.Core
  ( RenderRoute (..)
  )

-- | The class of sites which can read some data
class SiteReader r site | site -> r where
  {-# MINIMAL (ask | reader), local #-}
  -- | Get the data value
  ask :: (MonadSite m) => m site r
  ask = reader id
  -- | Extract a value from the data
  reader :: (MonadSite m) => (r -> a) -> m site a
  reader f = f <$> ask

  -- | Run a computation with a transformed version of the current data
  -- value
  local :: (MonadSite m) => (r -> r) -> m site a -> m site a

instance {-# OVERLAPPABLE #-}
  (SiteTrans t, SiteReader r site) => SiteReader r (t site) where
  ask = lift ask
  reader f = lift $ reader f

  local f = mapSiteT (local f)

-- | A site transformation which extends a site with some additional data
-- which can be read
data ReaderSite r site = ReaderSite
  { readVal :: r
  , unReaderSite :: site
  }

-- | Compute the effect of 'ReaderSite' by passing in the data value to be
-- used when reading
runReaderSite
  :: (MonadSite m)
  => r
  -> m (ReaderSite r site) a
  -> m site a
runReaderSite r
  = withSiteT (ReaderSite r)

instance Copointed (ReaderSite r) where
  copoint = unReaderSite

instance {-# OVERLAPPING #-} SiteReader r (ReaderSite r site) where
  ask = do
    ReaderSite r _ <- askSite
    pure r

  local f = withSiteT (\(ReaderSite r site) -> ReaderSite (f r) site)

instance RenderRoute site => RenderRoute (ReaderSite r site) where
  newtype Route (ReaderSite r site) = ReaderRoute (Route site)
  renderRoute (ReaderRoute route) = renderRoute route

deriving instance Eq (Route site) => Eq (Route (ReaderSite r site))

instance SiteTrans (ReaderSite r) where
  lift = withSiteT unReaderSite

  mapSiteT runner argM = do
    ReaderSite r _ <- askSite
    withSiteT unReaderSite $ runner $ withSiteT (ReaderSite r) argM