packages feed

fmr-0.1: src/Data/Field.hs

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
{-# LANGUAGE Safe, UndecidableInstances #-}

{- |
    License     :  BSD-style
    Module      :  Data.Field
    Copyright   :  (c) Andrey Mulik 2020
    Maintainer  :  work.a.mulik@gmail.com
    
    @Data.Field@ provides fake field type for record-style operations.
-}
module Data.Field
(
  -- * Simple field
  SField (..), SOField, sfield,
  
  -- * Field
  Field (..), OField,
  
  -- * Observable field
  Observe (..), observe
)
where

import qualified Data.List as L

import Data.Property
import Data.Functor

default ()

--------------------------------------------------------------------------------

-- | Simple field, which contain only getter and setter.
data SField m record a = SField
  {
    -- | 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

instance (InsertProp Field record many) => InsertProp SField record many
  where
    prependRecord x = prependRecord x . toField
    appendRecord  x = appendRecord  x . toField

instance (DeleteProp Field record many) => DeleteProp SField record many
  where
    deleteRecord x = deleteRecord x . toField

-- | 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)

toField :: (Monad m) => SField m record a -> Field m record a
toField field@(SField g s) = Field g s (modifyRecord field)

--------------------------------------------------------------------------------

-- | 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

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
  where
    incRecord record field = void $ modifyRecord record field not
    decRecord record field = void $ modifyRecord record field not
    
    switchRecord record field n = void $ modifyRecord record field (even n &&)

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 []
  where
    deleteRecord x record field = modifyRecord record field (L.delete x)

--------------------------------------------------------------------------------

-- | 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
  where
    incRecord field record = do
      incRecord (observed field) record
      onModify field record
    
    decRecord field record = do
      decRecord (observed field) record
      onModify field record
    
    switchRecord field record n = do
      switchRecord (observed field) record n
      onModify field record

instance (GetProp field record) => GetProp (Observe field) record
  where
    getRecord field record = do
      res <- getRecord (observed field) record
      onGet field record res
      return res

instance (SetProp field record) => SetProp (Observe field) record
  where
    setRecord field record val = do
      setRecord (observed field) record val
      onSet field record val

instance (ModifyProp field record) => ModifyProp (Observe field) record
  where
    modifyRecord field record upd = do
      res <- modifyRecord (observed field) record upd
      onModify field record
      return res

instance (InsertProp field record many) => InsertProp (Observe field) record many
  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

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