>
>module Object.Example where
>import Object.Templates
>import Object.Types
>import Object.Letters
>import Prelude hiding ((.))
>import Data.Maybe
>import qualified Data.Map as Map
>import Data.Map (Map)
In this example file we will see how to make objects
out of two data type 'Simple' and 'Foo x'
>data Simple = Simple { _simple :: Int } deriving (Show)
>data Foo x = Foo {
> _bar ::Int,
> _baz :: Char,
> _blub :: x
> } deriving (Show)
To make 'Foo x' we use templateHaskell.
>makeObject ''Foo
Poof, and we're done.
For 'Simple' we will do everything manually so you can see what happenes behind 'makeObject'.
We will want to make instances of the following two things that are defined in Object.Types.
type family Output object action
class Action object action where
(.) :: object -> action -> Output object action
Now 'makeObject' makes two types of 'Actions' actions called methods
(where action has type 'Method m' for some m) and actions called methodsets
(where action has type 'Method m := input).
Unless you want to use a closed type class later the following definition would not be neccessary.
So if you are new you can imagine 'Output' and 'MethodOutput' are the same thing. For the pros
the following two lines are not defined if you use 'makeObjectFlexible'.
>type instance Output Simple (Method m) = MethodOutput Simple (Method m)
>type instance Output Simple (Method m := input) = MethodOutput Simple (Method m := input)
First we need a method name for '_simple'
>type Simple_ = Method (T_s,T_i,T_m,T_p,T_l,T_e)
>simple = Method (T_s,T_i,T_m,T_p,T_l,T_e) :: Simple_
it is done this way so different modules can define the same name
you can use the macro makeName to produce the same thing.
So now we can implement a method we only need to make an instance of 'Action' and 'Output' or
since 'Output' was defered of 'MethodOutput'
the output will be an Int since _simple has type Int.
>type instance MethodOutput Simple Simple_ = Int
and the implementation will simply be a call to '_simple'
>instance Action Simple Simple_ where
> object . _ = _simple object
we can ignore the argument since we know it from the type
Now we can also make the methodset
>type instance MethodOutput Simple (Simple_ := anything) = Simple
>instance (anything ~ Int) => Action Simple (Simple_ := anything) where
> object . (_ := int) = object { _simple = int }
Notice how we match anything and only in the context make sure that it is in deed Int.
This is because other wise GHC would give some ambiguity error since someone else could
possibly add an instance
instance Action Simple (Simple_ := Float)
So let's see what we can do so far.
>fooAndSimple = do
> print$ Foo 1 'a' 1 . blub := 2 . blub
> print$ Simple 1 . simple
Pretty cool. Ok now something a bit more complicated for 'Data.Map'.
>type instance Output (Map k v) action = OutputMap (Map k v) action
>type family OutputMap map action where
> OutputMap (Map k v) (Method m) = MethodOutput (Map k v) (Method m)
> OutputMap (Map k v) [k' := v'] = (Map k v)
> OutputMap (Map k v) (k' := v') = (Map k v)
> OutputMap (Map k v) (Maybe k') = Maybe v
> OutputMap (Map k v) [k'] = [v]
> OutputMap (Map k v) k' = v
>makeName "size"
>type instance MethodOutput (Map k v) Size_ = Int
>instance Action (Map k v) Size_ where
> obj . _ = Map.size obj
>instance (k~k', v~v', OutputMap (Map k v) [k' := v'] ~ (Map k v), Ord k) => Action (Map k v) [k' := v'] where
> map . (k := v:rest) = Map.insert k v map . rest
> map . [] = map
>instance (k~k', v~v', OutputMap (Map k v) (k' := v') ~ (Map k v), Ord k) => Action (Map k v) (k' := v') where
> map . assign = map . [assign]
>instance (k~k', OutputMap (Map k v) (Maybe k') ~ (Maybe v), Ord k) => Action (Map k v) (Maybe k') where
> map . (Just key) = Map.lookup key map
> map . Nothing = Nothing
>instance (k~k', OutputMap (Map k v) [k'] ~ [v], Ord k) => Action (Map k v) [k'] where
> map . keys = catMaybes $ flip Map.lookup map <* keys
>instance (k~k', OutputMap (Map k v) k' ~ v, Ord k) => Action (Map k v) k' where
> map . key = fromMaybe (error "no element of this key in map") $ map . Just key
>mapExample = do
> let aMap = Map.empty . ['a' := "first_a", 'b' := "first_b"]
> print$ aMap . ('a' := "secont_a") . 'a'
> print$ aMap . ['a','b','c']
> print$ aMap . size
> print$ Map.empty . [(1::Int) := "hallo" ] . (1::Int)