{-# LANGUAGE CPP #-}
#if __GLASGOW_HASKELL__ >= 707
{-# LANGUAGE StandaloneDeriving, DeriveDataTypeable, Safe #-}
#elif __GLASGOW_HASKELL__ >= 702
{-# LANGUAGE Trustworthy #-}
#endif
-----------------------------------------------------------------------------
-- |
-- Module      :  Control.Comonad.Trans.Store
-- Copyright   :  (C) 2008-2013 Edward Kmett
-- License     :  BSD-style (see the file LICENSE)
--
-- Maintainer  :  Edward Kmett <ekmett@gmail.com>
-- Stability   :  provisional
-- Portability :  portable
--
--
-- The store comonad holds a constant value along with a modifiable /accessor/
-- function, which maps the /stored value/ to the /focus/.
--
-- This module defines the strict store (aka state-in-context/costate) comonad
-- transformer.
--
-- @stored value = (1, 5)@, @accessor = fst@, @resulting focus = 1@:
--
-- >>> :{
--  let
--    storeTuple :: Store (Int, Int) Int
--    storeTuple = store fst (1, 5)
-- :}
--
-- Add something to the focus:
--
-- >>> :{
--  let
--    addToFocus :: Int -> Store (Int, Int) Int -> Int
--    addToFocus x wa = x + extract wa
-- :}
--
-- >>> :{
--   let
--     added3 :: Store (Int, Int) Int
--     added3 = extend (addToFocus 3) storeTuple
-- :}
--
-- The focus of added3 is now @1 + 3 = 4@. However, this action changed only
-- the accessor function and therefore the focus but not the stored value:
--
-- >>> pos added3
-- (1,5)
--
-- >>> extract added3
-- 4
--
-- The strict store (state-in-context/costate) comonad transformer is subject
-- to the laws:
--
-- > x = seek (pos x) x
-- > y = pos (seek y x)
-- > seek y x = seek y (seek z x)
--
-- Thanks go to Russell O'Connor and Daniel Peebles for their help formulating
-- and proving the laws for this comonad transformer.
----------------------------------------------------------------------------
module Control.Comonad.Trans.Store
  (
  -- * The Store comonad
    Store, store, runStore
  -- * The Store comonad transformer
  , StoreT(..), runStoreT
  -- * Operations
  , pos
  , seek, seeks
  , peek, peeks
  , experiment
  ) where

#if __GLASGOW_HASKELL__ < 710
import Control.Applicative
#endif
import Control.Comonad
import Control.Comonad.Hoist.Class
import Control.Comonad.Trans.Class
import Data.Functor.Identity
#if !(MIN_VERSION_base(4,11,0))
import Data.Semigroup
#endif

#ifdef __GLASGOW_HASKELL__
import Data.Typeable

-- $setup
-- >>> import Control.Comonad
-- >>> import Data.Tuple (swap)

#if __GLASGOW_HASKELL__ >= 707
deriving instance Typeable StoreT
#else
instance (Typeable s, Typeable1 w) => Typeable1 (StoreT s w) where
  typeOf1 dswa = mkTyConApp storeTTyCon [typeOf (s dswa), typeOf1 (w dswa)]
    where
      s :: StoreT s w a -> s
      s = undefined
      w :: StoreT s w a -> w a
      w = undefined

instance (Typeable s, Typeable1 w, Typeable a) => Typeable (StoreT s w a) where
  typeOf = typeOfDefault

storeTTyCon :: TyCon
#if __GLASGOW_HASKELL__ < 704
storeTTyCon = mkTyCon "Control.Comonad.Trans.Store.StoreT"
#else
storeTTyCon = mkTyCon3 "comonad-transformers" "Control.Comonad.Trans.Store" "StoreT"
#endif
{-# NOINLINE storeTTyCon #-}
#endif

#endif

type Store s = StoreT s Identity

-- | Create a Store using an accessor function and a stored value
store :: (s -> a) -> s -> Store s a
store :: (s -> a) -> s -> Store s a
store s -> a
f s
s = Identity (s -> a) -> s -> Store s a
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT ((s -> a) -> Identity (s -> a)
forall a. a -> Identity a
Identity s -> a
f) s
s

runStore :: Store s a -> (s -> a, s)
runStore :: Store s a -> (s -> a, s)
runStore (StoreT (Identity s -> a
f) s
s) = (s -> a
f, s
s)

data StoreT s w a = StoreT (w (s -> a)) s

runStoreT :: StoreT s w a -> (w (s -> a), s)
runStoreT :: StoreT s w a -> (w (s -> a), s)
runStoreT (StoreT w (s -> a)
wf s
s) = (w (s -> a)
wf, s
s)

instance Functor w => Functor (StoreT s w) where
  fmap :: (a -> b) -> StoreT s w a -> StoreT s w b
fmap a -> b
f (StoreT w (s -> a)
wf s
s) = w (s -> b) -> s -> StoreT s w b
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT (((s -> a) -> s -> b) -> w (s -> a) -> w (s -> b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (a -> b
f (a -> b) -> (s -> a) -> s -> b
forall b c a. (b -> c) -> (a -> b) -> a -> c
.) w (s -> a)
wf) s
s

instance (ComonadApply w, Semigroup s) => ComonadApply (StoreT s w) where
  StoreT w (s -> a -> b)
ff s
m <@> :: StoreT s w (a -> b) -> StoreT s w a -> StoreT s w b
<@> StoreT w (s -> a)
fa s
n = w (s -> b) -> s -> StoreT s w b
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT ((s -> a -> b) -> (s -> a) -> s -> b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
(<*>) ((s -> a -> b) -> (s -> a) -> s -> b)
-> w (s -> a -> b) -> w ((s -> a) -> s -> b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> w (s -> a -> b)
ff w ((s -> a) -> s -> b) -> w (s -> a) -> w (s -> b)
forall (w :: * -> *) a b.
ComonadApply w =>
w (a -> b) -> w a -> w b
<@> w (s -> a)
fa) (s
m s -> s -> s
forall a. Semigroup a => a -> a -> a
<> s
n)

instance (Applicative w, Monoid s) => Applicative (StoreT s w) where
  pure :: a -> StoreT s w a
pure a
a = w (s -> a) -> s -> StoreT s w a
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT ((s -> a) -> w (s -> a)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (a -> s -> a
forall a b. a -> b -> a
const a
a)) s
forall a. Monoid a => a
mempty
  StoreT w (s -> a -> b)
ff s
m <*> :: StoreT s w (a -> b) -> StoreT s w a -> StoreT s w b
<*> StoreT w (s -> a)
fa s
n = w (s -> b) -> s -> StoreT s w b
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT ((s -> a -> b) -> (s -> a) -> s -> b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
(<*>) ((s -> a -> b) -> (s -> a) -> s -> b)
-> w (s -> a -> b) -> w ((s -> a) -> s -> b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> w (s -> a -> b)
ff w ((s -> a) -> s -> b) -> w (s -> a) -> w (s -> b)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> w (s -> a)
fa) (s -> s -> s
forall a. Monoid a => a -> a -> a
mappend s
m s
n)

instance Comonad w => Comonad (StoreT s w) where
  duplicate :: StoreT s w a -> StoreT s w (StoreT s w a)
duplicate (StoreT w (s -> a)
wf s
s) = w (s -> StoreT s w a) -> s -> StoreT s w (StoreT s w a)
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT ((w (s -> a) -> s -> StoreT s w a)
-> w (s -> a) -> w (s -> StoreT s w a)
forall (w :: * -> *) a b. Comonad w => (w a -> b) -> w a -> w b
extend w (s -> a) -> s -> StoreT s w a
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT w (s -> a)
wf) s
s
  extend :: (StoreT s w a -> b) -> StoreT s w a -> StoreT s w b
extend StoreT s w a -> b
f (StoreT w (s -> a)
wf s
s) = w (s -> b) -> s -> StoreT s w b
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT ((w (s -> a) -> s -> b) -> w (s -> a) -> w (s -> b)
forall (w :: * -> *) a b. Comonad w => (w a -> b) -> w a -> w b
extend (\w (s -> a)
wf' s
s' -> StoreT s w a -> b
f (w (s -> a) -> s -> StoreT s w a
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT w (s -> a)
wf' s
s')) w (s -> a)
wf) s
s
  extract :: StoreT s w a -> a
extract (StoreT w (s -> a)
wf s
s) = w (s -> a) -> s -> a
forall (w :: * -> *) a. Comonad w => w a -> a
extract w (s -> a)
wf s
s

instance ComonadTrans (StoreT s) where
  lower :: StoreT s w a -> w a
lower (StoreT w (s -> a)
f s
s) = ((s -> a) -> a) -> w (s -> a) -> w a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap ((s -> a) -> s -> a
forall a b. (a -> b) -> a -> b
$ s
s) w (s -> a)
f

instance ComonadHoist (StoreT s) where
  cohoist :: (forall x. w x -> v x) -> StoreT s w a -> StoreT s v a
cohoist forall x. w x -> v x
l (StoreT w (s -> a)
f s
s) = v (s -> a) -> s -> StoreT s v a
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT (w (s -> a) -> v (s -> a)
forall x. w x -> v x
l w (s -> a)
f) s
s

-- | Read the stored value
--
-- >>> pos $ store fst (1,5)
-- (1,5)
--
pos :: StoreT s w a -> s
pos :: StoreT s w a -> s
pos (StoreT w (s -> a)
_ s
s) = s
s

-- | Set the stored value
--
-- >>> pos . seek (3,7) $ store fst (1,5)
-- (3,7)
--
-- Seek satisfies the law
--
-- > seek s = peek s . duplicate
seek :: s -> StoreT s w a -> StoreT s w a
seek :: s -> StoreT s w a -> StoreT s w a
seek s
s ~(StoreT w (s -> a)
f s
_) = w (s -> a) -> s -> StoreT s w a
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT w (s -> a)
f s
s

-- | Modify the stored value
--
-- >>> pos . seeks swap $ store fst (1,5)
-- (5,1)
--
-- Seeks satisfies the law
--
-- > seeks f = peeks f . duplicate
seeks :: (s -> s) -> StoreT s w a -> StoreT s w a
seeks :: (s -> s) -> StoreT s w a -> StoreT s w a
seeks s -> s
f ~(StoreT w (s -> a)
g s
s) = w (s -> a) -> s -> StoreT s w a
forall s (w :: * -> *) a. w (s -> a) -> s -> StoreT s w a
StoreT w (s -> a)
g (s -> s
f s
s)

-- | Peek at what the current focus would be for a different stored value
--
-- Peek satisfies the law
--
-- > peek x . extend (peek y) = peek y
peek :: Comonad w => s -> StoreT s w a -> a
peek :: s -> StoreT s w a -> a
peek s
s (StoreT w (s -> a)
g s
_) = w (s -> a) -> s -> a
forall (w :: * -> *) a. Comonad w => w a -> a
extract w (s -> a)
g s
s


-- | Peek at what the current focus would be if the stored value was
--   modified by some function
peeks :: Comonad w => (s -> s) -> StoreT s w a -> a
peeks :: (s -> s) -> StoreT s w a -> a
peeks s -> s
f ~(StoreT w (s -> a)
g s
s) = w (s -> a) -> s -> a
forall (w :: * -> *) a. Comonad w => w a -> a
extract w (s -> a)
g (s -> s
f s
s)

-- | Applies a functor-valued function to the stored value, and then uses the
--   new accessor to read the resulting focus.
--
--   >>> let f x = if x > 0 then Just (x^2) else Nothing
--   >>> experiment f $ store (+1) 2
--   Just 5
--   >>> experiment f $ store (+1) (-2)
--   Nothing
experiment :: (Comonad w, Functor f) => (s -> f s) -> StoreT s w a -> f a
experiment :: (s -> f s) -> StoreT s w a -> f a
experiment s -> f s
f (StoreT w (s -> a)
wf s
s) = w (s -> a) -> s -> a
forall (w :: * -> *) a. Comonad w => w a -> a
extract w (s -> a)
wf (s -> a) -> f s -> f a
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> s -> f s
f s
s