fmr-0.2: src/Data/Field.hs
{-# LANGUAGE UndecidableSuperClasses, MultiParamTypeClasses, FlexibleInstances #-}
{-# LANGUAGE Trustworthy, TypeFamilies, FlexibleContexts, PatternSynonyms #-}
{- |
License : BSD-style
Module : Data.Field
Copyright : (c) Andrey Mulik 2020-2021
Maintainer : work.a.mulik@gmail.com
@Data.Field@ provides immutable field type for record-style operations.
-}
module Data.Field
(
-- * Field
Field (..), sfield,
GetterFor, SetterFor, ModifierFor, ModifierMFor,
-- * IsMVar and MonadVar
IsMVar (..), MonadVar (..)
)
where
import Prelude hiding ( (.), id )
import Data.Property
import Data.Typeable
import Data.Functor
import Data.IORef
import Data.STRef
import Data.Kind
import GHC.Conc
import Control.Concurrent.MVar
import Control.Category
import Control.Monad.ST
import Control.Monad
default ()
--------------------------------------------------------------------------------
{- |
Normal field, which contain getter, setter and modifier.
Since @fmr-0.2@, you can also combine fmr fields using @('.')@ and @'id'@ from
the 'Category' class:
@
outer :: (Monad m) => Field m outer inner
inner :: (Monad m) => Field m inner value
field :: (Monad m) => Field m outer value
field = outer.inner
@
-}
data Field m record a = Field
{
-- | Field getter
getField :: !(GetterFor m record a),
-- | Field setter
setField :: !(SetterFor m record a),
-- | Field modifier
modifyField :: !(ModifierFor m record a),
-- | Monadic field modifier
modifyFieldM :: !(ModifierMFor m record a)
} deriving ( Typeable )
-- | Getter type.
type GetterFor m record a = record -> m a
-- | Setter type.
type SetterFor m record a = record -> a -> m ()
-- | Modifier type.
type ModifierFor m record a = record -> (a -> a) -> m a
-- | Monadic modifier type.
type ModifierMFor m record a = record -> (a -> m a) -> m a
--------------------------------------------------------------------------------
-- | 'sfield' creates new field from given getter and setter.
sfield :: (Monad m) => GetterFor m record a -> SetterFor m record a -> Field m record a
sfield g s = Field g s (\ record f -> do res <- f <$> g record; s record res; return res)
(\ record go -> do res <- go =<< g record; s record res; return res)
--------------------------------------------------------------------------------
instance (Monad m) => Category (Field m)
where
Field g1 s1 m1 mm1 . Field g2 _ _ _ = Field (g1 <=< g2) s3 m3 mm3
where
mm3 record go = flip mm1 go =<< g2 record
m3 record f = flip m1 f =<< g2 record
s3 record value = flip s1 value =<< g2 record
id = Field return (\ _ _ -> return ()) (\ x f -> return (f x)) (flip ($))
--------------------------------------------------------------------------------
instance FieldGet Field where getRecord = getField
instance FieldSet Field where setRecord = setField
instance FieldModify Field where modifyRecord = modifyField
instance FieldSwitch Field
where
switchRecord field record = void .
modifyRecord field record . toggle
--------------------------------------------------------------------------------
{- |
The 'IsMVar' class provides 'this' field for entire record access.
Please note that you cannot create 'IsMVar' and 'MonadVar' instances for some
monad separately.
-}
class (Monad m, MonadVar m) => IsMVar m var
where
-- | 'this' is common variable access field.
this :: Field m (var a) a
-- | Create and initialize new mutable variable.
var :: a -> m (var a)
instance IsMVar (ST s) (STRef s)
where
var = newSTRef
this = Field readSTRef writeSTRef modify' modifyM'
where
modifyM' ref f = do res <- f =<< readSTRef ref; res <$ writeSTRef ref res
modify' ref f = do res <- f <$> readSTRef ref; res <$ writeSTRef ref res
instance IsMVar IO IORef
where
var = newIORef
this = Field readIORef writeIORef modify' modifyM'
where
modifyM' ref f = do val <- f =<< readIORef ref; writeIORef ref val; return val
modify' ref f = atomicModifyIORef' ref (\ a -> let b = f a in (b, b))
instance IsMVar IO MVar
where
var = newMVar
this = Field readMVar putMVar modify' modifyM'
where
modifyM' mvar f = mvar `modifyMVarMasked` \ a -> do b <- f a; return (b, b)
modify' mvar f = mvar `modifyMVar` \ a -> let b = f a in return (b, b)
instance IsMVar STM TVar
where
var = newTVar
this = Field readTVar writeTVar modifyTVar modifyMTVar
where
modifyMTVar tvar f = do res <- f =<< readTVar tvar; res <$ writeTVar tvar res
modifyTVar tvar f = do res <- f <$> readTVar tvar; res <$ writeTVar tvar res
--------------------------------------------------------------------------------
{- |
'MonadVar' is a class of monads for which defined at least one type of mutable
variable.
Note that a variable of type @(Var m)@ should be as simple possible for a
given monad. I only has to implement the basic operations triad: read,
write and update (which don't have to be atomic).
-}
class (Monad m, IsMVar m (Var m)) => MonadVar m
where
-- | @('Var' m)@ is type of mutable variable in monad @m@.
type Var m :: Type -> Type
instance MonadVar (ST s) where type Var (ST s) = STRef s
instance MonadVar IO where type Var IO = IORef
instance MonadVar STM where type Var STM = TVar