hommage-0.0.5: Sound/Hommage/Parameter.hs
-- | This module contains a datatype 'Field' and sone type classes. It is used
-- to construct record-like datatypes.
module Sound.Hommage.Parameter
(
-- | Example:
--
-- To create a \"record\" with the accessors\/modificators first you have to define the type
-- of the record, It must be a right-associative list of pairs, just like a LISP list.
-- The rightmost element must be @ () @. The following Example defines a record with information
-- for music generation.
--
-- > type DefaultNote = (Int, (Double, (Char, ())))
--
-- Then a default value should be defined.
--
-- > defaultNote = (0, (1.0, ('a' , ())))
-- > defaultNote :: DefaultNote
--
-- For each \"datafield\" the 'Field' must be declared.
--
-- > pitch :: Field DefaultNote Int
-- > volume :: Field DefaultNote Double
-- > channel :: Field DefaultNote Char
--
-- Creating the fields is done with 'toField' applied to a value of the \"record\" type.
--
-- > pitch :>> volume :>> channel :>> NilM = toFields defaultNote
--
-- Is is recommended to declare all types and match all results of 'toFields' incl. the 'NilM' at
-- the end.
--
-- NOTE:
--
-- > pitch :>> volume :>> channel :>> NilM = toFields (undefined :: DefaultNote)
--
-- should work too.
--
-- * Field
Field (..)
, (<>)
, fstField
, sndField
, UpdateField (..)
, CheckField (..)
, updateField
, checkField
, applyField
, withField
, Group (..)
, leftGroup
, rightGroup
, (><)
-- * Hetero List
, LM (..)
, NilM (..)
, PushFields (..)
, ToFields (..)
, PushGroups (..)
, ToGroups (..)
)
where
-------------------------------------------------------------------------------
infixr 5 :>>
data LM a b m = m a :>> b m
-- | The correct type is: @ data NilM (m :: * -> *) = NilM @ .
-- (But Haddock rejects this definition. GHC will need it)
--data NilM m = NilM
data NilM (m :: * -> *) = NilM
-------------------------------------------------------------------------------
-- | A @ Field record field @ is an interface to access a type @ field @ which
-- is part of a type @ record @.
-- A 'Field' is used to access and modify subtypes (\"datafields\") of
-- a record-like datastructure. In this module, a right-associative pair structure is
-- taken as record-type with \"datafields\" in the fst parts of the pairs.
data Field c a = Field
{ getField :: c -> a
, setField :: (a -> a) -> c -> c
}
(<>) :: Field a b -> Field c a -> Field c b
Field g1 f1 <> Field g2 f2 = Field (g1 . g2) (f2 . f1)
fstField :: Field (a,b) a
fstField = Field fst (\f (a,b) -> (f a, b))
sndField :: Field (a,b) b
sndField = Field snd (\f (a,b) -> (a, f b))
-------------------------------------------------------------------------------
data UpdateField c = forall a . Field c a := a
| forall a . Field c a :$ (a -> a)
| If (CheckField c) [UpdateField c] [UpdateField c]
data CheckField c = forall a . Field c a :? (a -> Bool)
| And [CheckField c]
| Or [CheckField c]
updateField :: UpdateField c -> c -> c
updateField (ctxt := a) = setField ctxt (const a)
updateField (ctxt :$ f) = setField ctxt f
updateField (If ch u e) = \c -> if checkField ch c then applyField u c else applyField e c
checkField :: CheckField c -> c -> Bool
checkField (ctxt :? p) c = p $ getField ctxt c
checkField (And cs) c = and $ map (flip checkField c) cs
checkField (Or cs) c = or $ map (flip checkField c) cs
applyField :: [UpdateField c] -> c -> c
applyField us c = foldl (flip updateField) c us
withField :: [UpdateField a] -> (a -> b) -> a -> b
withField ucs f = f . applyField ucs
-------------------------------------------------------------------------------
class PushFields m where
pushFields :: m (Field c) -> m (Field (x, c))
instance PushFields NilM where
pushFields NilM = NilM
instance PushFields b => PushFields (LM a b) where
pushFields (ca :>> bc) = (ca <> sndField) :>> pushFields bc
-------------------------------------------------------------------------------
class ToFields a r where
toFields :: a -> r (Field a)
instance ToFields () NilM where
toFields _ = NilM
instance (PushFields b', ToFields b b') => ToFields (a, b) (LM a b') where
-- toField (a, b) = fstField :>> pushFields (toField b)
toFields _ = fstField :>> pushFields (toFields undefined)
--instance (PushFields b', ToField b b') => ToField (L a b) (LM a b') where
-- toField (a :> b) = fstField :>> pushFields (toField b)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
data Group c a = Group
{ isGroup :: c -> Maybe a
, mkGroup :: a -> c
}
leftGroup :: Group (Either a b) a
leftGroup = Group (either Just (const Nothing)) Left
rightGroup :: Group (Either a b) b
rightGroup = Group (either (const Nothing) Just) Right
(><) :: Group a b -> Group c a -> Group c b
Group i1 t1 >< Group i2 t2 = Group (\c -> i2 c >>= i1) (t2 . t1)
-------------------------------------------------------------------------------
class PushGroups m where
pushGroups :: m (Group c) -> m (Group (Either x c))
instance PushGroups NilM where
pushGroups NilM = NilM
instance PushGroups b => PushGroups (LM a b) where
pushGroups (ca :>> bc) = (ca >< rightGroup) :>> pushGroups bc
-------------------------------------------------------------------------------
class ToGroups a r where
toGroups :: a -> r (Group a)
instance ToGroups () NilM where
toGroups _ = NilM
instance (ToGroups b b', PushGroups b') => ToGroups (Either a b) (LM a b') where
toGroups eab = leftGroup :>> pushGroups (toGroups (either undefined id eab))
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
type DefaultNote1 = (Int, (Double, (Char, ())))
defaultNote1 :: DefaultNote1
defaultNote1 = (0, (1.0, ('a' , ())))
pitch1 :: Field DefaultNote1 Int
volume1 :: Field DefaultNote1 Double
channel1 :: Field DefaultNote1 Char
pitch1 :>> volume1 :>> channel1 :>> NilM = toFields defaultNote1
-------------------------------------------------------------------------------
type DefaultNote2 = (Int, (Double, ()))
defaultNote2 :: DefaultNote2
defaultNote2 = (0, (1.0, ()))
pitch2 :: Field DefaultNote2 Int
volume2 :: Field DefaultNote2 Double
pitch2 :>> volume2 :>> NilM = toFields defaultNote2
-------------------------------------------------------------------------------
type MixNote = Either DefaultNote1 (Either DefaultNote2 ())
n1 :: Group MixNote DefaultNote1
n2 :: Group MixNote DefaultNote2
n1 :>> n2 :>> NilM = toGroups (undefined :: MixNote)
-------------------------------------------------------------------------------
--data Prop c = forall a . Field c a := a
--def@[ volume := 2.0, trigger := True ] = definition
-------------------------------------------------------------------------------
{-
-------------------------------------------------------------------------------
data Output t a = Output { unOutput :: a -> t }
-------------------------------------------------------------------------------
class PushOutputs m where
pushOutputs :: m (Output c) -> m (Output (Either x c))
instance PushOutputs NilM where
pushOutputs NilM = NilM
instance PushOutputs b => PushOutputs (LM a b) where
pushOutputs (ca :>> bc) = (Output (Right . unOutput ca)) :>> pushOutputs bc
-------------------------------------------------------------------------------
class ToOutput a r where
toOutput :: a -> r (Output a)
instance ToOutput () NilM where
toOutput _ = NilM
instance (ToOutput b b', PushOutputs b') => ToOutput (Either a b) (LM a b') where
toOutput _ = Output Left :>> pushOutputs (toOutput undefined)
-------------------------------------------------------------------------------
data Filter t a = Filter { unFilter :: t -> Maybe a }
-------------------------------------------------------------------------------
class PushFilters m where
pushFilters :: m (Filter c) -> m (Filter (Either x c))
instance PushFilters NilM where
pushFilters NilM = NilM
instance PushFilters b => PushFilters (LM a b) where
pushFilters (ca :>> bc) = (Filter (\c -> either (const Nothing) Just c >>= unFilter ca)) :>> pushFilters bc
-------------------------------------------------------------------------------
class ToFilter a r where
toFilter :: a -> r (Filter a)
instance ToFilter () NilM where
toFilter _ = NilM
instance (ToFilter b b', PushFilters b') => ToFilter (Either a b) (LM a b') where
toFilter _ = Filter (either Just (const Nothing)) :>> pushFilters (toFilter undefined)
-------------------------------------------------------------------------------
-}