fmr 0.1 → 0.2
raw patch · 5 files changed
+939/−249 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- Data.Field: Observe :: field m record a -> (record -> a -> m ()) -> (record -> a -> m ()) -> (record -> m ()) -> Observe field m record a
- Data.Field: SField :: !record -> m a -> !record -> a -> m () -> SField m record a
- Data.Field: [getSField] :: SField m record a -> !record -> m a
- Data.Field: [observed] :: Observe field m record a -> field m record a
- Data.Field: [onGet] :: Observe field m record a -> record -> a -> m ()
- Data.Field: [onModify] :: Observe field m record a -> record -> m ()
- Data.Field: [onSet] :: Observe field m record a -> record -> a -> m ()
- Data.Field: [setSField] :: SField m record a -> !record -> a -> m ()
- Data.Field: data Observe field m record a
- Data.Field: data SField m record a
- Data.Field: instance Data.Property.DeleteProp Data.Field.Field record []
- Data.Field: instance Data.Property.DeleteProp Data.Field.Field record many => Data.Property.DeleteProp Data.Field.SField record many
- Data.Field: instance Data.Property.DeleteProp field record many => Data.Property.DeleteProp (Data.Field.Observe field) record many
- Data.Field: instance Data.Property.GetProp Data.Field.Field record
- Data.Field: instance Data.Property.GetProp Data.Field.SField record
- Data.Field: instance Data.Property.GetProp field record => Data.Property.GetProp (Data.Field.Observe field) record
- Data.Field: instance Data.Property.InsertProp Data.Field.Field record []
- Data.Field: instance Data.Property.InsertProp Data.Field.Field record many => Data.Property.InsertProp Data.Field.SField record many
- Data.Field: instance Data.Property.InsertProp field record many => Data.Property.InsertProp (Data.Field.Observe field) record many
- Data.Field: instance Data.Property.ModifyProp Data.Field.Field record
- Data.Field: instance Data.Property.ModifyProp Data.Field.SField record
- Data.Field: instance Data.Property.ModifyProp field record => Data.Property.ModifyProp (Data.Field.Observe field) record
- Data.Field: instance Data.Property.SetProp Data.Field.Field record
- Data.Field: instance Data.Property.SetProp Data.Field.SField record
- Data.Field: instance Data.Property.SetProp field record => Data.Property.SetProp (Data.Field.Observe field) record
- Data.Field: instance Data.Property.SwitchProp Data.Field.Field GHC.Types.Bool
- Data.Field: instance Data.Property.SwitchProp Data.Field.Field record => Data.Property.SwitchProp Data.Field.SField record
- Data.Field: instance Data.Property.SwitchProp field a => Data.Property.SwitchProp (Data.Field.Observe field) a
- Data.Field: instance GHC.Real.Integral switch => Data.Property.SwitchProp Data.Field.Field switch
- Data.Field: observe :: Monad m => field m record a -> Observe field m record a
- Data.Field: type OField = Observe Field
- Data.Field: type SOField = Observe SField
- Data.Property: [:+=] :: (Monad m, InsertProp field record many) => a -> field m record (many a) -> Prop m field record
- Data.Property: [::=] :: (Monad m, SetProp field record) => field m record a -> (record -> a) -> Prop m field record
- Data.Property: [::~] :: (Monad m, ModifyProp field record) => field m record a -> (record -> a -> a) -> Prop m field record
- Data.Property: [:=+] :: (Monad m, InsertProp field record many) => field m record (many a) -> a -> Prop m field record
- Data.Property: [:=] :: (Monad m, SetProp field record) => field m record a -> a -> Prop m field record
- Data.Property: [:~=] :: (Monad m, DeleteProp field record many, Eq a) => field m record (many a) -> a -> Prop m field record
- Data.Property: [:~] :: (Monad m, ModifyProp field record) => field m record a -> (a -> a) -> Prop m field record
- Data.Property: [Decr] :: (Monad m, SwitchProp field a) => field m record a -> Prop m field record
- Data.Property: [Incr] :: (Monad m, SwitchProp field a) => field m record a -> Prop m field record
- Data.Property: [Switch] :: (Monad m, SwitchProp field a) => field m record a -> Int -> Prop m field record
- Data.Property: appendRecord :: (InsertProp field record many, Monad m) => a -> field m record (many a) -> record -> m (many a)
- Data.Property: class DeleteProp field record many
- Data.Property: class GetProp field record
- Data.Property: class InsertProp field record many
- Data.Property: class ModifyProp field record
- Data.Property: class SetProp field record
- Data.Property: class SwitchProp field a
- Data.Property: decRecord :: (SwitchProp field a, Monad m) => field m record a -> record -> m ()
- Data.Property: deleteRecord :: (DeleteProp field record many, Monad m, Eq a) => a -> field m record (many a) -> record -> m (many a)
- Data.Property: incRecord :: (SwitchProp field a, Monad m) => field m record a -> record -> m ()
- Data.Property: prependRecord :: (InsertProp field record many, Monad m) => a -> field m record (many a) -> record -> m (many a)
- Data.Property: setProp :: Monad m => record -> [Prop m field record] -> m ()
+ Data.Field: -- <tt>m</tt>.
+ Data.Field: -- | <tt>(<a>Var</a> m)</tt> is type of mutable variable in monad
+ Data.Field: [modifyFieldM] :: Field m record a -> !ModifierMFor m record a
+ Data.Field: class (Monad m, MonadVar m) => IsMVar m var
+ Data.Field: class (Monad m, IsMVar m (Var m)) => MonadVar m where {
+ Data.Field: instance Data.Field.IsMVar (GHC.ST.ST s) (GHC.STRef.STRef s)
+ Data.Field: instance Data.Field.IsMVar GHC.Conc.Sync.STM GHC.Conc.Sync.TVar
+ Data.Field: instance Data.Field.IsMVar GHC.Types.IO GHC.IORef.IORef
+ Data.Field: instance Data.Field.IsMVar GHC.Types.IO GHC.MVar.MVar
+ Data.Field: instance Data.Field.MonadVar (GHC.ST.ST s)
+ Data.Field: instance Data.Field.MonadVar GHC.Conc.Sync.STM
+ Data.Field: instance Data.Field.MonadVar GHC.Types.IO
+ Data.Field: instance Data.Property.FieldGet Data.Field.Field
+ Data.Field: instance Data.Property.FieldModify Data.Field.Field
+ Data.Field: instance Data.Property.FieldSet Data.Field.Field
+ Data.Field: instance Data.Property.FieldSwitch Data.Field.Field
+ Data.Field: instance GHC.Base.Monad m => Control.Category.Category (Data.Field.Field m)
+ Data.Field: this :: IsMVar m var => Field m (var a) a
+ Data.Field: type GetterFor m record a = record -> m a
+ Data.Field: type ModifierFor m record a = record -> (a -> a) -> m a
+ Data.Field: type ModifierMFor m record a = record -> (a -> m a) -> m a
+ Data.Field: type SetterFor m record a = record -> a -> m ()
+ Data.Field: type family Var m :: Type -> Type;
+ Data.Field: var :: IsMVar m var => a -> m (var a)
+ Data.Field: }
+ Data.Field.Observe: Observe :: field m record a -> (record -> a -> m ()) -> (record -> a -> m ()) -> (record -> m ()) -> Observe field m record a
+ Data.Field.Observe: [observed] :: Observe field m record a -> field m record a
+ Data.Field.Observe: [onGet] :: Observe field m record a -> record -> a -> m ()
+ Data.Field.Observe: [onModify] :: Observe field m record a -> record -> m ()
+ Data.Field.Observe: [onSet] :: Observe field m record a -> record -> a -> m ()
+ Data.Field.Observe: data Observe field m record a
+ Data.Field.Observe: instance (Data.Property.FieldModify field, Data.Property.FieldGet field) => Data.Property.FieldModify (Data.Field.Observe.Observe field)
+ Data.Field.Observe: instance Data.Property.FieldGet field => Data.Property.FieldGet (Data.Field.Observe.Observe field)
+ Data.Field.Observe: instance Data.Property.FieldSet field => Data.Property.FieldSet (Data.Field.Observe.Observe field)
+ Data.Field.Observe: instance Data.Property.FieldSwitch field => Data.Property.FieldSwitch (Data.Field.Observe.Observe field)
+ Data.Field.Observe: observe :: Monad m => field m record a -> Observe field m record a
+ Data.MField: [MField] :: MonadVar m => (record -> MFieldRef m a) -> MField m record a
+ Data.MField: class IsRef ref
+ Data.MField: data MField m record a
+ Data.MField: data MFieldRef m a
+ Data.MField: data MFieldRep m a
+ Data.MField: getter :: MField m record a -> Field m record (m a)
+ Data.MField: instance Data.MField.IsRef Data.MField.MFieldRef
+ Data.MField: instance Data.MField.IsRef Data.MField.MFieldRep
+ Data.MField: instance Data.Property.FieldGet Data.MField.MField
+ Data.MField: instance Data.Property.FieldModify Data.MField.MField
+ Data.MField: instance Data.Property.FieldSet Data.MField.MField
+ Data.MField: instance Data.Property.FieldSwitch Data.MField.MField
+ Data.MField: link :: (IsRef ref, MonadVar m) => Var m a -> m (ref m a)
+ Data.MField: modifier :: MField m record a -> Field m record ((a -> a) -> m a)
+ Data.MField: modifierM :: MField m record a -> Field m record ((a -> m a) -> m a)
+ Data.MField: ref :: (IsRef ref, MonadVar m) => ref m a -> MFieldRef m a
+ Data.MField: setter :: MField m record a -> Field m record (a -> m ())
+ Data.Property: [Prop] :: (Monad m, IsProp prop) => prop m field record -> Prop m field record
+ Data.Property: class FieldGet field
+ Data.Property: class (FieldSet field) => FieldModify field
+ Data.Property: class FieldSet field
+ Data.Property: class FieldSwitch field
+ Data.Property: class IsProp (prop :: PropertyKind)
+ Data.Property: class IsSwitch switch
+ Data.Property: decr :: (Monad m, FieldSwitch field, IsSwitch a) => field m record a -> Prop m field record
+ Data.Property: gets' :: (Monad m, FieldGet field) => record -> [field m record a] -> m [a]
+ Data.Property: incr :: (Monad m, FieldSwitch field, IsSwitch a) => field m record a -> Prop m field record
+ Data.Property: instance Data.Property.IsProp Data.Property.ModifyProp
+ Data.Property: instance Data.Property.IsProp Data.Property.SetProp
+ Data.Property: instance Data.Property.IsProp Data.Property.SwitchProp
+ Data.Property: instance Data.Property.IsSwitch GHC.Types.Bool
+ Data.Property: instance GHC.Real.Integral i => Data.Property.IsSwitch i
+ Data.Property: modifyRecordM :: (FieldModify field, Monad m, FieldGet field) => field m record a -> record -> (a -> m a) -> m a
+ Data.Property: pattern (:~<$) :: (Typeable m, Typeable field, Typeable record, Monad m, FieldModify field, FieldGet field) => [field m record a] -> (record -> a -> m a) -> Prop m field record
+ Data.Property: pattern (:~<) :: (Typeable m, Typeable field, Typeable record, Monad m, FieldModify field, FieldGet field) => field m record a -> (record -> a -> m a) -> Prop m field record
+ Data.Property: performProp :: (IsProp prop, Monad m) => record -> prop m field record -> m ()
+ Data.Property: sets' :: Monad m => record -> [Prop m field record] -> m ()
+ Data.Property: switch :: (Monad m, FieldSwitch field, IsSwitch a) => field m record a -> Int -> Prop m field record
+ Data.Property: toggle :: IsSwitch switch => Int -> switch -> switch
+ Data.Property: type FieldKind = (Type -> Type) -> Type -> Type -> Type
+ Data.Property: type PropertyKind = (Type -> Type) -> FieldKind -> Type -> Type
- Data.Field: Field :: !record -> m a -> !record -> a -> m () -> !record -> (a -> a) -> m a -> Field m record a
+ Data.Field: Field :: !GetterFor m record a -> !SetterFor m record a -> !ModifierFor m record a -> !ModifierMFor m record a -> Field m record a
- Data.Field: [getField] :: Field m record a -> !record -> m a
+ Data.Field: [getField] :: Field m record a -> !GetterFor m record a
- Data.Field: [modifyField] :: Field m record a -> !record -> (a -> a) -> m a
+ Data.Field: [modifyField] :: Field m record a -> !ModifierFor m record a
- Data.Field: [setField] :: Field m record a -> !record -> a -> m ()
+ Data.Field: [setField] :: Field m record a -> !SetterFor m record a
- Data.Field: sfield :: Monad m => (record -> m a) -> (record -> a -> m ()) -> Field m record a
+ Data.Field: sfield :: Monad m => GetterFor m record a -> SetterFor m record a -> Field m record a
- Data.Property: get :: (Monad m, GetProp field record) => field m record a -> record -> m a
+ Data.Property: get :: (Monad m, FieldGet field) => field m record a -> record -> m a
- Data.Property: getRecord :: (GetProp field record, Monad m) => field m record a -> record -> m a
+ Data.Property: getRecord :: (FieldGet field, Monad m) => field m record a -> record -> m a
- Data.Property: modifyRecord :: (ModifyProp field record, Monad m) => field m record a -> record -> (a -> a) -> m a
+ Data.Property: modifyRecord :: (FieldModify field, Monad m) => field m record a -> record -> (a -> a) -> m a
- Data.Property: setRecord :: (SetProp field record, Monad m) => field m record a -> record -> a -> m ()
+ Data.Property: setRecord :: (FieldSet field, Monad m) => field m record a -> record -> a -> m ()
- Data.Property: switchRecord :: (SwitchProp field a, Monad m) => field m record a -> record -> Int -> m ()
+ Data.Property: switchRecord :: (FieldSwitch field, Monad m, IsSwitch a) => field m record a -> record -> Int -> m ()
Files
- fmr.cabal +20/−5
- src/Data/Field.hs +122/−137
- src/Data/Field/Observe.hs +80/−0
- src/Data/MField.hs +155/−0
- src/Data/Property.hs +562/−107
fmr.cabal view
@@ -1,9 +1,9 @@ name: fmr-version: 0.1+version: 0.2 category: Syntax -synopsis: Fake monadic records library-description: The generalized syntax of abstract monadic records.+synopsis: Fields for Monadic Records library+description: The generalized, extensible syntax of abstract monadic records. author: Andrey Mulik maintainer: <work.a.mulik@gmail.com>@@ -16,9 +16,21 @@ build-type: Simple cabal-version: >=1.10 +tested-with:+ -- lts-7.24+ GHC == 8.0.1,+ -- lts-9.21+ GHC == 8.0.2,+ -- lts-11.22+ GHC == 8.2.2,+ -- lts-12.26+ GHC == 8.4.4,+ -- lts-18.8+ GHC == 8.10.6+ source-repository head type: git- location: https://github.com/andreymulik/fake-records+ location: https://github.com/andreymulik/fmr --- _ _____ ______ ______ ___ ______ __ __ --- --- | | |_ _|| ___ \| ___ \ / _ \ | ___ \\ \ / / ---@@ -31,10 +43,13 @@ default-language: Haskell2010 hs-source-dirs: src - build-depends: base >= 4.12 && < 5 + build-depends: base >= 4.9 && < 5 ghc-options: -Wall -Wcompat exposed-modules: Data.Property+ Data.MField+ + Data.Field.Observe Data.Field
src/Data/Field.hs view
@@ -1,190 +1,175 @@-{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}-{-# LANGUAGE Safe, UndecidableInstances #-}+{-# LANGUAGE UndecidableSuperClasses, MultiParamTypeClasses, FlexibleInstances #-}+{-# LANGUAGE Trustworthy, TypeFamilies, FlexibleContexts, PatternSynonyms #-} {- | License : BSD-style Module : Data.Field- Copyright : (c) Andrey Mulik 2020+ Copyright : (c) Andrey Mulik 2020-2021 Maintainer : work.a.mulik@gmail.com - @Data.Field@ provides fake field type for record-style operations.+ @Data.Field@ provides immutable field type for record-style operations. -} module Data.Field (- -- * Simple field- SField (..), SOField, sfield,- -- * Field- Field (..), OField,+ Field (..), sfield,+ GetterFor, SetterFor, ModifierFor, ModifierMFor, - -- * Observable field- Observe (..), observe+ -- * IsMVar and MonadVar+ IsMVar (..), MonadVar (..) ) where -import qualified Data.List as L-+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 () -------------------------------------------------------------------------------- --- | Simple field, which contain only getter and setter.-data SField m record a = SField+{- |+ 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 {- -- | Get field value- getSField :: !(record -> m a),- -- | Set field value- setSField :: !(record -> a -> m ())- }---- | 'Observe' 'SField'.-type SOField = Observe SField--instance GetProp SField record where getRecord = getSField-instance SetProp SField record where setRecord = setSField-instance ModifyProp SField record--instance (SwitchProp Field record) => SwitchProp SField record- where- switchRecord = switchRecord . toField- incRecord = incRecord . toField- decRecord = decRecord . toField+ -- | 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 ) -instance (InsertProp Field record many) => InsertProp SField record many- where- prependRecord x = prependRecord x . toField- appendRecord x = appendRecord x . toField+-- | Getter type.+type GetterFor m record a = record -> m a -instance (DeleteProp Field record many) => DeleteProp SField record many- where- deleteRecord x = deleteRecord x . toField+-- | Setter type.+type SetterFor m record a = record -> a -> m () --- | Create 'Field' from getter and setter.-sfield :: (Monad m) => (record -> m a) -> (record -> a -> m ()) -> Field m record a-sfield g s = toField (SField g s)+-- | Modifier type.+type ModifierFor m record a = record -> (a -> a) -> m a -toField :: (Monad m) => SField m record a -> Field m record a-toField field@(SField g s) = Field g s (modifyRecord field)+-- | Monadic modifier type.+type ModifierMFor m record a = record -> (a -> m a) -> m a -------------------------------------------------------------------------------- --- | Normal field, which contain getter, setter and modifier.-data Field m record a = Field- {- -- | Get field value- getField :: !(record -> m a),- -- | Set field value- setField :: !(record -> a -> m ()),- -- | Modify field value- modifyField :: !(record -> (a -> a) -> m a)- }---- | Observable 'Field'.-type OField = Observe Field--instance GetProp Field record where getRecord = getField-instance SetProp Field record where setRecord = setField-instance ModifyProp Field record where modifyRecord = modifyField+-- | '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 (Integral switch) => SwitchProp Field switch- where- incRecord field record = void $ modifyRecord field record succ- decRecord field record = void $ modifyRecord field record pred- - switchRecord field record = void . modifyRecord field record . (+) . fromIntegral+-------------------------------------------------------------------------------- -instance {-# INCOHERENT #-} SwitchProp Field Bool+instance (Monad m) => Category (Field m) where- incRecord record field = void $ modifyRecord record field not- decRecord record field = void $ modifyRecord record field not+ 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 - switchRecord record field n = void $ modifyRecord record field (even n &&)+ id = Field return (\ _ _ -> return ()) (\ x f -> return (f x)) (flip ($)) -instance InsertProp Field record []- where- appendRecord x record field = modifyRecord record field (++ [x])- prependRecord x record field = modifyRecord record field (x :)+-------------------------------------------------------------------------------- -instance DeleteProp Field record []+instance FieldGet Field where getRecord = getField+instance FieldSet Field where setRecord = setField+instance FieldModify Field where modifyRecord = modifyField+instance FieldSwitch Field where- deleteRecord x record field = modifyRecord record field (L.delete x)+ switchRecord field record = void .+ modifyRecord field record . toggle -------------------------------------------------------------------------------- --- | Simple field observer, which can run some handlers after each action.-data Observe field m record a = Observe- {- -- | Field to observe.- observed :: field m record a,- -- | 'getRecord' observer- onGet :: record -> a -> m (),- -- | 'setRecord' observer- onSet :: record -> a -> m (),- -- | 'modifyRecord' observer- onModify :: record -> m ()- }---- | Create field with default observers.-observe :: (Monad m) => field m record a -> Observe field m record a-observe field =- let nothing = \ _ _ -> return ()- in Observe field nothing nothing (\ _ -> return ())--instance (SwitchProp field a) => SwitchProp (Observe field) a+{- |+ 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- incRecord field record = do- incRecord (observed field) record- onModify field record- - decRecord field record = do- decRecord (observed field) record- onModify field record+ -- | 'this' is common variable access field.+ this :: Field m (var a) a - switchRecord field record n = do- switchRecord (observed field) record n- onModify field record+ -- | Create and initialize new mutable variable.+ var :: a -> m (var a) -instance (GetProp field record) => GetProp (Observe field) record+instance IsMVar (ST s) (STRef s) where- getRecord field record = do- res <- getRecord (observed field) record- onGet field record res- return res+ 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 (SetProp field record) => SetProp (Observe field) record+instance IsMVar IO IORef where- setRecord field record val = do- setRecord (observed field) record val- onSet field record val+ 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 (ModifyProp field record) => ModifyProp (Observe field) record+instance IsMVar IO MVar where- modifyRecord field record upd = do- res <- modifyRecord (observed field) record upd- onModify field record- return res+ 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 (InsertProp field record many) => InsertProp (Observe field) record many+instance IsMVar STM TVar where- prependRecord x field record = do- res <- prependRecord x (observed field) record- onModify field record- return res- - appendRecord x field record = do- res <- appendRecord x (observed field) record- onModify field record- return res+ 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 -instance (DeleteProp field record many) => DeleteProp (Observe field) record many- where- deleteRecord x field record = do- res <- deleteRecord x (observed field) record- onModify field record- return 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
+ src/Data/Field/Observe.hs view
@@ -0,0 +1,80 @@+{- |+ License : BSD-style+ Module : Data.Field.Observe+ Copyright : (c) Andrey Mulik 2021+ Maintainer : work.a.mulik@gmail.com+ + @Data.Field.Observe@ simple field observer.+-}+module Data.Field.Observe+(+ -- * Exports+ module Data.Property,+ + -- * Observable field+ Observe (..), observe+)+where++import Data.Typeable+import Data.Property++default ()++--------------------------------------------------------------------------------++-- | Simple field observer, which can run some handlers after each action.+data Observe field m record a = Observe+ {+ -- | Field to observe.+ observed :: field m record a,+ -- | 'getRecord' observer+ onGet :: record -> a -> m (),+ -- | 'setRecord' observer+ onSet :: record -> a -> m (),+ -- | 'modifyRecord' observer+ onModify :: record -> m ()+ }+ deriving ( Typeable )++-- | Create field with default observers.+observe :: (Monad m) => field m record a -> Observe field m record a+observe field =+ let og = \ _ _ -> return (); om = \ _ -> return (); os = og+ in Observe field og os om++--------------------------------------------------------------------------------++instance (FieldSwitch field) => FieldSwitch (Observe field)+ where+ switchRecord field record n = do+ switchRecord (observed field) record n+ onModify field record++instance (FieldGet field) => FieldGet (Observe field)+ where+ getRecord field record = do+ res <- getRecord (observed field) record+ onGet field record res+ return res++instance (FieldSet field) => FieldSet (Observe field)+ where+ setRecord field record val = do+ setRecord (observed field) record val+ onSet field record val++instance (FieldModify field, FieldGet field) => FieldModify (Observe field)+ where+ modifyRecord field record upd = do+ res <- modifyRecord (observed field) record upd+ onModify field record+ return res+ + modifyRecordM field record upd = do+ res <- modifyRecordM (observed field) record upd+ onModify field record+ return res+++
+ src/Data/MField.hs view
@@ -0,0 +1,155 @@+{-# LANGUAGE Safe, GADTs #-}++{- |+ License : BSD-style+ Module : Data.MField+ Copyright : (c) Andrey Mulik 2020-2021+ Maintainer : work.a.mulik@gmail.com+ + @Data.MField@ provides mutable field type for record-style operations.+-}+module Data.MField+(+ -- * Exports+ module Data.Property,+ module Data.Field,+ + -- * Mutable field+ MField (..), IsRef (..), MFieldRef, MFieldRep,+ getter, setter, modifier, modifierM+)+where++import Data.Typeable+import Data.Property+import Data.Field++import Control.Monad++default ()++--------------------------------------------------------------------------------++{- |+ @since 0.2+ + 'MField' represents field with mutable value and accessors. Unlike fields of+ type 'Field', 'MField' fields are undesirable (and sometimes impossible) to be+ stored in the global environment. The dynamic nature of 'MField' means+ changing accessors for specific records, so accessors do not contain a record+ argument.+ + 'MField' isn't designed for accessing external abstract objects and interfaces,+ it doesn't give the same freedom as 'Field', but at the same time it allows+ you to create flexible connections between fields and values without having to+ reconcile them.+ + 'MField' contains a function that takes a record and returns an 'MFieldRef'.+ + Example:+ + @+ newtype HasField a = HasField {someField :: MFieldRep IO a}+ + field :: MField IO (HasField a) a+ field = MField (ref.someField)+ + main :: IO ()+ main = do+ putStrLn "Put some number"+ record <- fmap HasField $ link =<< var =<< readLn :: IO (HasField Integer)+ print =<< get field record+ + set record [getter field :~ fmap (* 2)]+ print =<< get field record+ @+-}+data MField m record a+ where+ MField :: (MonadVar m) => (record -> MFieldRef m a) -> MField m record a+ deriving ( Typeable )++instance FieldGet MField+ where+ getRecord (MField field) = join.get this.getRef.field++instance FieldSet MField+ where+ setRecord (MField field) record value = do+ g <- get this.setRef.field $ record; g value++instance FieldModify MField+ where+ modifyRecord (MField field) record upd = do+ m <- get this.modifyRef.field $ record; m upd+ + modifyRecordM (MField field) record go = do+ mm <- get this.modifyMRef.field $ record; mm go++instance FieldSwitch MField+ where+ switchRecord field record = void.modifyRecord field record.toggle++--------------------------------------------------------------------------------++class IsRef ref+ where+ -- | Create new reference to the given variable.+ link :: (MonadVar m) => Var m a -> m (ref m a)+ + -- | Get an associated 'MFieldRef' reference to the given reference.+ ref :: (MonadVar m) => ref m a -> MFieldRef m a++--------------------------------------------------------------------------------++{- |+ 'MFieldRef' is a structure containing accessors for a specific field of a+ specific record. As a rule, it makes sense to store 'MFieldRef' in the record+ to which it refers.+-}+data MFieldRef m a = MFieldRef+ {+ getRef :: !(Var m (m a)),+ setRef :: !(Var m (a -> m ())),+ modifyRef :: !(Var m ((a -> a) -> m a)),+ modifyMRef :: !(Var m ((a -> m a) -> m a))+ } deriving ( Typeable )++instance IsRef MFieldRef+ where+ ref = id+ link x = liftM4 MFieldRef (var (g x)) (var (s x)) (var (m x)) (var (mm x))+ where+ Field g s m mm = this++getter :: MField m record a -> Field m record (m a)+getter (MField f) =+ let Field g s m mm = this+ in Field (g.getRef.f) (s.getRef.f) (m.getRef.f) (mm.getRef.f)++setter :: MField m record a -> Field m record (a -> m ())+setter (MField f) =+ let Field g s m mm = this+ in Field (g.setRef.f) (s.setRef.f) (m.setRef.f) (mm.setRef.f)++modifier :: MField m record a -> Field m record ((a -> a) -> m a)+modifier (MField f) =+ let Field g s m mm = this+ in Field (g.modifyRef.f) (s.modifyRef.f) (m.modifyRef.f) (mm.modifyRef.f)++modifierM :: MField m record a -> Field m record ((a -> m a) -> m a)+modifierM (MField f) =+ let Field g s m mm = this+ in Field (g.modifyMRef.f) (s.modifyMRef.f) (m.modifyMRef.f) (mm.modifyMRef.f)++--------------------------------------------------------------------------------++-- | 'MFieldRep' is a helper type that stores a variable and its accessors.+data MFieldRep m a = MFieldRep !(Var m a) {-# UNPACK #-} !(MFieldRef m a)+ deriving ( Typeable )++instance IsRef MFieldRep+ where+ link x = MFieldRep x <$> link x+ ref (MFieldRep _ fr) = fr+
src/Data/Property.hs view
@@ -1,165 +1,620 @@-{-# LANGUAGE Safe, DefaultSignatures, GADTs, UndecidableInstances, BangPatterns #-}-{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances #-}+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, UndecidableInstances #-}+{-# LANGUAGE Safe, GADTs, DefaultSignatures, KindSignatures, DataKinds, CPP #-}+{-# LANGUAGE PatternSynonyms, ViewPatterns #-} +-- For [ghc-8.0 .. ghc-8.4) support.+#if __GLASGOW_HASKELL__ < 840+{-# LANGUAGE TypeInType #-}+#endif+ {- | License : BSD-style Module : Data.Property Copyright : (c) Andrey Mulik 2020 Maintainer : work.a.mulik@gmail.com - @Data.Property@ provides property type 'Prop' for record-style operations.+ @Data.Property@ new-style properties. -} module Data.Property (- -- * Property- Prop (..), get, set, setProp,+ -- * Generalized properties+ IsProp (..), PropertyKind, FieldKind, Prop (..), - -- ** Get- GetProp (..),+ -- ** Basic properties+ FieldGet (..), get, gets',+ FieldSet (..), set, sets', - -- ** Set- SetProp (..),+ -- *** Set properties+ pattern (:=), pattern (::=), pattern (:=$), pattern (::=$), - -- ** Modify- ModifyProp (..), InsertProp (..), DeleteProp (..), SwitchProp (..)+ -- *** Monadic set properties+ pattern (:<=), pattern (:<=$), pattern (:=<), pattern (:=<$),+ + -- ** Modify properties+ FieldModify (..),+ + -- *** Modify properties+ pattern (:~), pattern (:~$), pattern (::~), pattern (::~$),+ + -- *** Monadic modify properties+ pattern (:<~), pattern (:<~$), pattern (:~<), pattern (:~<$),+ + -- ** Switch properties+ IsSwitch (..), FieldSwitch (..), switch, incr, decr ) where -import Data.Functor+import Data.Typeable+import Data.Kind +import Control.Monad+ default () -------------------------------------------------------------------------------- --- | Property representation+{- Generalized property. -}++{- |+ @since 0.2+ 'Prop' is new, generalized and extensible property type (existential), which+ may contain any 'IsProp' value.+-} data Prop m field record where- -- | Increase property value (switch flag, increment, etc.).- Incr :: (Monad m, SwitchProp field a) =>- field m record a -> Prop m field record- - -- | Decrease property value (switch flag, decrement, etc.).- Decr :: (Monad m, SwitchProp field a) =>- field m record a -> Prop m field record- - -- | Increase/decrease value many times.- Switch :: (Monad m, SwitchProp field a) =>- field m record a -> Int -> Prop m field record+ Prop :: (Monad m, IsProp prop) => prop m field record -> Prop m field record+ deriving ( Typeable )++{- |+ @since 0.2+ 'IsProp' is a property class that allows you to extend @fmr@ syntax. Now you+ can create new property types and use it with existing in 'set' list of+ actions.+-}+class IsProp (prop :: PropertyKind)+ where+ -- | @performProp record prop @ performs an action on @record@ using @prop@.+ performProp :: (Monad m) => record -> prop m field record -> m ()++instance IsProp SetProp+ where+ performProp record (SetRecordPropM field kl) = setRecords field record =<< kl record+ performProp record (SetRecordProp field f) = setRecords field record (f record)+ performProp record (SetPropM field mval) = setRecords field record =<< mval+ performProp record (SetProp field val) = setRecords field record val++setRecords :: (Monad m, FieldSet field) => [field m record a] -> record -> a -> m ()+setRecords fields record val = forM_ fields $ \ field -> setRecord field record val++instance IsProp ModifyProp+ where+ performProp record (Modify field f) = () <$ modifyRecords field record (f record)+ performProp record (ModifyM field f) = () <$ modifyRecordsM field record (f record)+ performProp record (ModifyProp field f) = () <$ modifyRecords field record f+ performProp record (ModifyPropM field f) = () <$ modifyRecordsM field record f++modifyRecords :: (Monad m, FieldModify field) =>+ [field m record b] -> record -> (b -> b) -> m ()+modifyRecords fields record f = fields `forM_` \ field -> modifyRecord field record f++modifyRecordsM :: (Monad m, FieldModify field, FieldGet field) =>+ [field m record b] -> record -> (b -> m b) -> m ()+modifyRecordsM fields record f = fields `forM_` \ field -> modifyRecordM field record f++instance IsProp SwitchProp+ where+ performProp record (SwitchProp n field) = switchRecord field record n++--------------------------------------------------------------------------------++-- | @since 0.2 Service kind synonym.+type FieldKind = (Type -> Type) -> Type -> Type -> Type++-- | @since 0.2 Service kind synonym.+type PropertyKind = (Type -> Type) -> FieldKind -> Type -> Type++--------------------------------------------------------------------------------++{- fmr classes. -}++-- | @since 0.2 Property getter class.+class FieldGet field+ where+ -- | @'getRecord' field record@ return @record@'s value using @field@.+ getRecord :: (Monad m) => field m record a -> record -> m a++-- | @since 0.2 Property setter class.+class FieldSet field+ where+ -- | @'setRecord' field record value@ sets new @record@ @value@.+ setRecord :: (Monad m) => field m record a -> record -> a -> m ()++{- |+ @since 0.2+ Property modifier class.+ + Note that 'FieldModifier' doesn't go well with write-only fields, because+ 'modifyRecord' returns new value, and 'modifyRecordM' also assumes the+ possibility of old value \"leaking\" (hence the 'FieldGet' constraint is+ imposed on it).+-}+class (FieldSet field) => FieldModify field+ where+ {- |+ @'modifyRecord' field record upd@ modifies @record@ @field@ using @upd@.+ Returns new value.+ -}+ default modifyRecord :: (Monad m, FieldGet field) =>+ field m record a -> record -> (a -> a) -> m a+ modifyRecord :: (Monad m) => field m record a -> record -> (a -> a) -> m a+ modifyRecord field record f = do+ val <- f <$> get field record+ setRecord field record val+ return val - -- | @field ':=' val@ sets new value @val@ to record.- (:=) :: (Monad m, SetProp field record) =>- field m record a -> a -> Prop m field record+ {- |+ @'modifyRecordM' field record upd@ modifies @record@ @field@ using @upd@.+ Note that assumes the possibility of old value \"leaking\", e.g.:+ + @+ -- get value using 'modifyRecordM'+ getLeak = do+ -- Some read-only fields+ x <- newWriteOnly+ y <- 'var' Nothing+ -- write current value to y and do not modify.+ modifyRecordM this x (\ val -> do set [this := Just val]; return val)+ -- leaking: return current value of "write-only" record+ get this y+ @+ + So you cannot use it for write-only fields.+ -}+ modifyRecordM :: (Monad m, FieldGet field) =>+ field m record a -> record -> (a -> m a) -> m a+ modifyRecordM field record f = do+ val <- f =<< get field record+ setRecord field record val+ return val++{- |+ Switch property modifier.+ + Note that 'FieldSwitch' is designed for deterministic switches with a+ pre-known set of states and order of transitions between them, without+ branches. If you need more complex behavior, use 'FieldModify'.+-}+class FieldSwitch field+ where+ -- | Repeated increment or decrement.+ switchRecord :: (Monad m, IsSwitch a) => field m record a -> record -> Int -> m ()++--------------------------------------------------------------------------------++{- fmr properties. -}++{- |+ @since 0.2+ 'SetProp' is a service type used to set field values.+ See @(':=')@, @(':=$')@, @('::=')@, @('::=$')@, @(':<=')@, @(':<=$')@,+ @(':=<')@ and @(':=<$')@ patterns.+-}+data SetProp m field record+ where+ -- | 'SetProp' corresponds to @(':=$')@ and @(':=')@.+ SetProp :: (FieldSet field) =>+ [field m record a] -> a -> SetProp m field record - -- | @field '::=' upd@ - updates value using @upd@.- (::=) :: (Monad m, SetProp field record) =>- field m record a -> (record -> a) -> Prop m field record+ -- | 'SetPropM' corresponds to @('::=$')@ and @('::=')@.+ SetPropM :: (FieldSet field) =>+ [field m record a] -> m a -> SetProp m field record - -- | @field ':~' upd@ updates value using @upd@.- (:~) :: (Monad m, ModifyProp field record) =>- field m record a -> (a -> a) -> Prop m field record+ -- | 'SetRecordProp' corresponds to @(':<=$')@ and @(':<=')@.+ SetRecordProp :: (FieldSet field) =>+ [field m record a] -> (record -> a) -> SetProp m field record - -- | @field '::=' upd@ - updates value using @upd@.- (::~) :: (Monad m, ModifyProp field record) =>- field m record a -> (record -> a -> a) -> Prop m field record+ -- | 'SetRecordPropM' corresponds to @(':=<$')@ and @(':=<')@.+ SetRecordPropM :: (FieldSet field) =>+ [field m record a] -> (record -> m a) -> SetProp m field record+ deriving ( Typeable )++{- |+ @since 0.2+ 'ModifyProp' is a service type used to modify field values.+ See @(':~')@, @(':~$')@, @(':<~')@, @(':<~$')@, @('::~')@, @('::~$')@,+ @(':~<')@, @(':~<$')@ patterns.+-}+data ModifyProp m field record+ where+ -- | 'FieldModify' constructor corresponds to @(':~$')@ and @(':~')@.+ ModifyProp :: (FieldModify field) =>+ [field m record a] -> (a -> a) -> ModifyProp m field record - -- | @field ':=+' val@ appends @val@ to @record@ value.- (:=+) :: (Monad m, InsertProp field record many) =>- field m record (many a) -> a -> Prop m field record+ -- | 'FieldModify' constructor corresponds to @(':<~$')@ and @(':<~')@.+ ModifyPropM :: (FieldModify field, FieldGet field) =>+ [field m record a] -> (a -> m a) -> ModifyProp m field record - -- | @val ':=+' field@ prepends @val@ to @record@ value.- (:+=) :: (Monad m, InsertProp field record many) =>- a -> field m record (many a) -> Prop m field record+ -- | 'Modify' constructor corresponds to @('::~$')@ and @('::~')@.+ Modify :: (FieldModify field) =>+ [field m record a] -> (record -> a -> a) -> ModifyProp m field record - -- | @field ':~=' val@ removes @val@ from @record@ value- (:~=) :: (Monad m, DeleteProp field record many, Eq a) =>- field m record (many a) -> a -> Prop m field record+ -- | 'ModifyM' constructor corresponds to @(':~<$')@ and @(':~<')@.+ ModifyM :: (FieldModify field, FieldGet field) =>+ [field m record a] -> (record -> a -> m a) -> ModifyProp m field record+ deriving ( Typeable ) +{- |+ @since 0.2+ 'SwitchProp' is a service type used to update record values, see 'switch',+ 'incr' and 'decr'.+-}+data SwitchProp m field record+ where+ -- | Switch field, see 'switch', 'incr' and 'decr'.+ SwitchProp :: (Monad m, FieldSwitch field, IsSwitch a) =>+ Int -> field m record a -> SwitchProp m field record+ deriving ( Typeable )+ -------------------------------------------------------------------------------- --- | 'getRecord' shortcut.-get :: (Monad m, GetProp field record) => field m record a -> record -> m a-get = getRecord+{- fmr pure setters. -} --- | 'setProp' shortcut.-set :: (Monad m) => record -> [Prop m field record] -> m ()-set = setProp+{- |+ Pure value setter. @set record [field := value]@ set @value@ to @record@'s+ @field@.+-}+pattern (:=) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldSet field+ ) => field m record a -> a -> Prop m field record+pattern field := val = [field] :=$ val --- | @setRecord record props@ changes @record@ value using @props@ properties.-setProp :: (Monad m) => record -> [Prop m field record] -> m ()-setProp record = mapM_ $ \ prop -> case prop of- Incr field -> incRecord field record- Decr field -> decRecord field record- Switch field n -> switchRecord field record n+{- |+ Pure value setter with @record@. @set record [field ::= f]@ set @f record@ to+ @record@'s @field@. - field ::= upd -> setRecord field record (upd record)- field := val -> setRecord field record val- field ::~ upd -> void $ modifyRecord field record (upd record)- field :~ upd -> void $ modifyRecord field record upd+ @+ set record [field ::= const val] === set record [field := val]+ @+-}+pattern (::=) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldSet field+ ) => field m record a -> (record -> a) -> Prop m field record+pattern field ::= f = [field] ::=$ f++{- |+ @since 0.2+ Pure group setter. @set record [fields :=$ value]@ set @value@ to @record@'s+ some @fields@. - field :=+ val -> void $ prependRecord val field record- val :+= field -> void $ appendRecord val field record- field :~= val -> void $ deleteRecord val field record+ @+ set record [[field] :=$ value] === set record [field := value]+ @+-}+pattern (:=$) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldSet field+ ) => [field m record a] -> a -> Prop m field record+pattern field :=$ val <- (cast -> Just (SetProp field val))+ where+ (:=$) = Prop ... SetProp +{- |+ @since 0.2+ Pure group setter with @record@. @set record [fields ::=$ f]@ set @f record@+ to @record@'s some @fields@.+ + @+ set record [[field] ::=$ f] === set record [field ::= f]+ set record [fields ::=$ const val] === set record [fields :=$ val]+ @+-}+pattern (::=$) ::+ (+ Typeable m, Typeable field, Typeable record, Monad m, FieldSet field+ ) => [field m record a] -> (record -> a) -> Prop m field record+pattern field ::=$ f <- (cast -> Just (SetRecordProp field f))+ where+ (::=$) = Prop ... SetRecordProp+ -------------------------------------------------------------------------------- --- | Property getter.-class GetProp field record+{- fmr pure updaters. -}++{- |+ Pure value modifier. @set record [field :~ f]@ modify value of @record@'s+ @field@ using @f@ function.+ + @+ set record [field :~ const val] === set record [field := val]+ @+-}+pattern (:~) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldModify field+ ) => field m record a -> (a -> a) -> Prop m field record+pattern field :~ f = [field] :~$ f++{- |+ Pure value modifier with @record@. @set record [field ::~ f]@ modify value of+ @record@'s @field@ using @f record@ function.+ + @+ set record [field ::~ const f] === set record [field :~ f]+ @+-}+pattern (::~) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldModify field+ ) => field m record a -> (record -> a -> a) -> Prop m field record+pattern field ::~ f = [field] ::~$ f++{- |+ @since 0.2+ Pure group modifier. @set record [fields :~$ f]@ modify values of @record@'s+ @fields@ using @f@ function.+ + @+ set record [[field] :~$ val] === set record [field :~ val]+ set record [fields :~$ const val] === set record [field :=$ val]+ @+-}+pattern (:~$) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldModify field+ ) => [field m record a] -> (a -> a) -> Prop m field record+pattern field :~$ f <- (cast -> Just (ModifyProp field f)) where- -- | @'getRecord' field record@ return @record@'s value using @field@.- getRecord :: (Monad m) => field m record a -> record -> m a+ (:~$) = Prop ... ModifyProp --- | Property setter.-class SetProp field record+{- |+ @since 0.2+ Pure group modifier with @record@.+ @set record [fields ::~$ f]@ modify values of @record@'s @fields@ using+ @f record@ function.+ + @+ set record [[field] ::~$ f] === set record [field ::~ f]+ set record [fields ::~$ const f] === set record [field :~$ f]+ @+-}+pattern (::~$) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldModify field+ ) => [field m record a] -> (record -> a -> a) -> Prop m field record+pattern field ::~$ f <- (cast -> Just (Modify field f)) where- -- | @'setRecord' field record value@ sets new @record@ @value@.- setRecord :: (Monad m) => field m record a -> record -> a -> m ()+ (::~$) = Prop ... Modify --- | Property modifier.-class ModifyProp field record+--------------------------------------------------------------------------------++{- fmr monadic setters. -}++{- |+ @since 0.2+ Monadic value setter. @set record [field :<= mvalue]@ set result of @mvalue@+ to @record@'s @field@. Note that the @mvalue@ is evaluated every time a+ @field@ value is assigned.+ + @+ set record [field :<= return val] === set record [field := val]+ set record [field :<= mval] === do val <- mval; set record [field :<= val]+ @+-}+pattern (:<=) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldSet field+ ) => field m record a -> m a -> Prop m field record+pattern field :<= mval = [field] :<=$ mval++{- |+ @since 0.2+ Monadic value setter with @record@. @set record [field :=< mvalue]@ set result+ of @mvalue record@ to @record@'s @field@. Note that the @mvalue@ is evaluated+ every time a @field@ value is assigned.+ + @+ set record [field :=< const val] === set record [field :<= val]+ set record [field :=< f] === do val <- f record; set record [field := val]+ @+-}+pattern (:=<) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldSet field+ ) => field m record a -> (record -> m a) -> Prop m field record+pattern field :=< f = [field] :=<$ f++{- |+ @since 0.2+ Monadic group setter. @set record [fields :<=$ mvalue]@ set result of @mvalue@+ to @record@'s @fields@. Note that @mvalue@ is evaluated only once, on the+ first assignment. Thus, the values of all the listed fields will be identical.+ + @+ set record [[field] :<=$ const f] === set record [field :<= val]+ set record [fields :<=$ mval] === do val <- mval; set record [fields :<=$ val]+ @+-}+pattern (:<=$) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldSet field+ ) => [field m record a] -> m a -> Prop m field record+pattern field :<=$ mval <- (cast -> Just (SetPropM field mval)) where- -- | @'modifyRecord' field record upd@ modifies @record@ @field@ using @upd@.- default modifyRecord :: (Monad m, GetProp field record, SetProp field record) =>- field m record a -> record -> (a -> a) -> m a- modifyRecord :: (Monad m) => field m record a -> record -> (a -> a) -> m a- modifyRecord field record f = do- old <- get field record- let new = f old- setRecord field record new- return new+ (:<=$) = Prop ... SetPropM +{- |+ @since 0.2+ Monadic group setter with @record@. @set record [fields :=<$ f]@ set result of+ @f record@ to @record@'s @fields@. Note that @f record@ is evaluated only+ once, on the first assignment. Thus, the values of all the listed fields will+ be identical.+ + @+ set record [[field] :=<$ f] === set record [field :=< val]+ set record [fields :=<$ const val] = set record [fields :=$ val]+ set record [fields :=<$ f] === do val <- f record; set record [fields :=$ val]+ @+-}+pattern (:=<$) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldSet field+ ) => [field m record a] -> (record -> m a) -> Prop m field record+pattern field :=<$ f <- (cast -> Just (SetRecordPropM field f))+ where+ (:=<$) = Prop ... SetRecordPropM+ -------------------------------------------------------------------------------- --- | Switch property modifier.-class SwitchProp field a+{- fmr monadic updaters. -}++{- |+ @since 0.2+ Monadic value modifier. @set record [field :<~ f]@ modifies value of+ @record@'s @field@ using @f@ procedure. Note that the @mvalue@ is called every+ time a @field@ value is assigned.+ + @+ set record [field :<~ return val] === set record [fields := val]+ @+-}+pattern (:<~) ::+ (+ Typeable m, Typeable field, Typeable record, Monad m,+ FieldModify field, FieldGet field+ ) => field m record a -> (a -> m a) -> Prop m field record+pattern field :<~ f = [field] :<~$ f++{- |+ @since 0.2+ Monadic value modifier with @record@. @set record [field :<~ f]@ modifies+ value of @record@'s @field@ using @f record@ procedure. Note that the+ @f record@ is called every time a @field@ value is assigned.+ + @+ set record [field :~< const f] === set record [fields :<~ f]+ @+-}+pattern (:~<) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldModify field, FieldGet field+ ) => field m record a -> (record -> a -> m a) -> Prop m field record+pattern field :~< f = [field] :~<$ f++{- |+ @since 0.2+ Monadic group modifier. @set record [fields :<~$ f]@ modifies values of+ @record@'s @fields@ using @f@ procedure.+ + @+ set record [[field] :<~$ f] === set record [field :<~ f]+ set record [fields :<~$ const mval] === set record [field :<=$ mval]+ @+-}+pattern (:<~$) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldModify field, FieldGet field+ ) => [field m record a] -> (a -> m a) -> Prop m field record+pattern field :<~$ f <- (cast -> Just (ModifyPropM field f)) where- -- | Generalized increment, same as @switchRecord@.- incRecord :: (Monad m) => field m record a -> record -> m ()- - -- | Generalized decrement.- decRecord :: (Monad m) => field m record a -> record -> m ()- + (:<~$) = Prop ... ModifyPropM++{- |+ @since 0.2+ Monadic group modifier with @record@. @set record [fields :~<$ f]@ modifies+ values of @record@'s @fields@ using @f record@ procedure. Note that the+ @f record@ is called every time a @field@ value is assigned.+ + @+ set record [field :~<$ const f] === set record [fields :<~$ f]+ @+-}+pattern (:~<$) ::+ (+ Typeable m, Typeable field, Typeable record,+ Monad m, FieldModify field, FieldGet field+ ) => [field m record a] -> (record -> a -> m a) -> Prop m field record+pattern field :~<$ f <- (cast -> Just (ModifyM field f))+ where+ (:~<$) = Prop ... ModifyM++--------------------------------------------------------------------------------++-- | Service class for switchable types.+class IsSwitch switch+ where {- |- Increment/decrement many times.+ @'toggle' s n@ "toggles" the state represented by the value @s@ by @n@+ positions, for example: - > decRecord field record = switchRecord field record (-1)- > incRecord field record = switchRecord field record 1+ @+ toggle n False = even n+ toggle n True = odd n+ toggle n 1234 = 1239 + n+ @ -}- switchRecord :: (Monad m) => field m record a -> record -> Int -> m ()+ toggle :: Int -> switch -> switch --- | Prepend/append modifier.-class InsertProp field record many+instance IsSwitch Bool where- -- | Prepends new element to existing value.- prependRecord :: (Monad m) => a -> field m record (many a) -> record -> m (many a)- - -- | Appends new element to existing value.- appendRecord :: (Monad m) => a -> field m record (many a) -> record -> m (many a)+ toggle n False = even n+ toggle n True = odd n --- | Delete modifier.-class DeleteProp field record many- where- -- | Delete element from value (if any).- deleteRecord :: (Monad m, Eq a) => a -> field m record (many a) -> record -> m (many a)+instance (Integral i) => IsSwitch i where toggle n i = fromIntegral n + i +-------------------------------------------------------------------------------- +-- | The 'get' function reads current value of a field.+get :: (Monad m, FieldGet field) => field m record a -> record -> m a+get = getRecord++-- | @'gets'' fields record@ returns list of @record@ @fields@ values.+gets' :: (Monad m, FieldGet field) => record -> [field m record a] -> m [a]+gets' = mapM . flip getRecord++{- |+ 'set' is the main function in @fmr@, which allows you to describe changing the+ value of a record as a sequence of operations on its fields, e.g.+-}+set :: (Monad m) => record -> [Prop m field record] -> m ()+set record = mapM_ $ \ (Prop prop) -> performProp record prop++-- | Just synonym for 'set'.+sets' :: (Monad m) => record -> [Prop m field record] -> m ()+sets' = set++-- | 'switch' changes the value by n steps.+switch :: (Monad m, FieldSwitch field, IsSwitch a) =>+ field m record a -> Int -> Prop m field record+switch field n = Prop (SwitchProp n field)++-- | @'incr' field@ is same as @switch field 1@.+incr :: (Monad m, FieldSwitch field, IsSwitch a) =>+ field m record a -> Prop m field record+incr = Prop . SwitchProp 1++-- | @'decr' field@ is same as @switch field (-1)@.+decr :: (Monad m, FieldSwitch field, IsSwitch a) =>+ field m record a -> Prop m field record+decr = Prop . SwitchProp (-1)++--------------------------------------------------------------------------------++-- | @sdp@ @(.)@-like combinator.+(...) :: (c -> d) -> (a -> b -> c) -> a -> b -> d+(...) = (.) . (.)