packages feed

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 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+(...) =  (.) . (.)