{-# LANGUAGE OverlappingInstances #-} module Data.Has.Engine where import Data.Has.TypeList -- | @Field a@ is a type list which contains only one element of -- @a@. And every field in the records should be this type. -- -- If you concatenate fields with @(:&:)@ at type-level, @(&)@ at -- value-level, it becomes a record can be manipulated by functions -- in this module. type Field a = a ::: TyNil -- | Creates a 'Field' of @a@. field :: a -> Field a field a = a ::: TyNil -- | Concatenates between 'Field's or records. Records are -- concatenated rows. For example, Following expressions are -- valid. -- -- > -- Concatenation of rows (i.e. record) -- > field "string" & field True -- -- > -- Concatenation of records -- > (field 'c' & field ()) & (field False & field "string") -- -- > -- ... And concatenations between a field and a record -- > field () & (field False & field "string") -- > (field 'c' & field ()) & field False (&) :: (Append a b) => a -> b -> a :&: b (&) = (.++.) infixr 5 & -- | Represents concatenated rows or records. type family a :&: b type instance a :&: b = a :++: b infixr 5 :&: -- | Provides injection and projection into type lists. -- -- Holds @e == prj (inj e s)@ for all @s@ and @e@. class Contains e s where -- | Injects a value of type @e@ into @s@ if @s@ contains the type @e@. inj :: e -> s -> s -- | Projects a value of type @e@ out from @s@ if @s@ contains the type @e@. prj :: s -> e instance Contains e (e ::: r) where inj e ~(_ ::: r) = e ::: r prj ~(e' ::: _) = e' instance Contains e r => Contains e (h ::: r) where inj e ~(h ::: r) = h ::: inj e r prj ~(_ ::: r) = prj r -- | Updates a value @e@ in @s@, using given function @e -> e@. upd :: (Contains e s) => (e -> e) -> s -> s upd f s = let e = prj s in inj (f e) s