hoop-0.3.0.0: src/Language/MSH/Selectors.hs
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses, KindSignatures #-}
{-# LANGUAGE Rank2Types, FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds, FlexibleContexts #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
module Language.MSH.Selectors where
--------------------------------------------------------------------------------
import Control.Applicative ((<$>))
import Control.Monad.Identity
import Control.Monad.State
--------------------------------------------------------------------------------
-- | Represents a query which can be run by combinators such as `result',
-- `object', etc.
data RunnableQuery obj st ctx r where
MkExtCall :: ctx (r, obj) -> RunnableQuery obj st ctx r
-- | `result` @selector obtains the result from invoking @selector.
result :: (ctx ~ Identity) => RunnableQuery obj st ctx r -> r
result (MkExtCall call) = fst $ runIdentity call
-- | `object` @selector obtains an updated object from invoking @selector.
object :: (ctx ~ Identity) => RunnableQuery obj st ctx r -> obj
object (MkExtCall call) = snd $ runIdentity call
--value :: Selector Field o s m a -> a
--value (MkField eg _ _ _) = eg
-- | Enumerates types of object class members.
data MemberType = Method | Field
-- | Determines the type of selector that results from composing two
-- selectors.
type family MemberComposeResult (lhs :: MemberType) (rhs :: MemberType) :: MemberType where
MemberComposeResult Method Method = Method
MemberComposeResult Method Field = Method
MemberComposeResult Field Method = Method
MemberComposeResult Field Field = Field
--type Selector ty o s m a =
-- | A selector is a container for object class members.
data Selector (ty :: MemberType) o s m a where
MkMethod :: StateT s m a ->
(o -> m (a, o)) ->
Selector Method o s m a
MkField :: (o -> m (a, o)) ->
StateT s m a ->
(o -> a -> m ((), o)) ->
(a -> StateT s m ()) ->
Selector Field o s m a
-- | Proxy type used to capture the type variables of an object.
data This o s (m :: * -> *) a where
MkThis :: This o s m a
type family QueryObject obj :: *
type family QueryMonad obj (m :: * -> *) :: * -> *
type family QueryResult obj (ty :: MemberType) st (m :: * -> *) r :: *
infixr 8 .!
class Monad m => Object obj st m where
this :: This obj st m obj
this = MkThis
(.!) :: forall r ty. obj ->
Selector ty (QueryObject obj) st {-(QueryMonad obj m)-} m r ->
QueryResult obj ty st m r
-- | If `s' returns a value whose type is a `Functor', then `s.$m' calls `m' on the
-- inner value of `s' via `fmap'.
(.$) :: (Monad ctx, Functor f) =>
Selector lty obj st ctx (f a) ->
Selector rty a st' Identity b ->
Selector (MemberComposeResult lty rty) obj st ctx (f b)
(MkField eg ig es is) .$ (MkMethod ri re) = MkMethod
(do box <- ig
let r = fmap (runIdentity . re) box
is (fmap snd r)
return (fmap fst r))
(\this -> do (box,this') <- eg this
let r = fmap (runIdentity . re) box
(_,this'') <- es this' (fmap snd r)
return (fmap fst r, this''))
(MkField eg ig es is) .$ (MkField reg rig res ris) = MkField
(\this -> do (box,this') <- eg this
let r = fmap (runIdentity . reg) box
(_,this'') <- es this' (fmap snd r)
return (fmap fst r, this''))
(do box <- ig
let r = fmap (runIdentity . reg) box
is (fmap snd r)
return (fmap fst r))
-- TODO: the definition of Selector is too restrictive to
-- implement the following two cases: we need to split the 'a' type
-- parameter into two: one for the type of values to get and one for
-- the type of values to set
undefined
{-(\this v -> do (box,this') <- eg this
let r = fmap (\obj -> runIdentity (res obj v)) box
(_,this'') <- es this' (fmap snd r)
return ((), this''))-}
undefined
{-(\v -> do box <- ig
)-}
(MkMethod li le) .$ (MkMethod _ re) = MkMethod
(do box <- li
let r = fmap (runIdentity . re) box
return (fmap fst r))
(\this -> do (box, this') <- le this
let r = fmap (runIdentity . re) box
return (fmap fst r, this'))
(MkMethod li le) .$ (MkField reg _ _ _) = MkMethod
(do box <- li
let r = fmap (runIdentity . reg) box
return (fmap fst r))
(\this -> do (box, this') <- le this
let r = fmap (runIdentity . reg) box
return (fmap fst r, this'))
-- For fields:
-- * run the internal call (if the selector on the RHS is a method)
-- * run the internal getter (if the selector on the RHS is a field)
type instance QueryMonad (Selector Method obj st ctx r) ctx' = ctx'
type instance QueryObject (Selector Method obj st ctx r) = r
type instance QueryResult (Selector Method obj st ctx r) ty st' m x =
Selector Method obj st ctx x
instance (Object obj st ctx, Object r st' Identity, m ~ Identity) =>
Object (Selector Method obj st ctx r) st' m where
(.!) (MkMethod li le) (MkMethod ri re) = MkMethod
(do obj <- li
let (r,obj') = runIdentity (re obj)
return r)
(\this -> do (obj,this') <- le this
let (r,obj') = runIdentity (re obj)
return (r, this'))
(.!) (MkMethod li le) (MkField eg ig es is) = MkMethod
(do obj <- li
let (r,obj') = runIdentity (eg obj)
return r)
(\this -> do (obj, this') <- le this
let (r,obj') = runIdentity (eg obj)
return (r, this'))
-- For fields:
-- * run the internal call (if the selector on the RHS is a method)
-- * run the internal getter (if the selector on the RHS is a field)
type instance QueryMonad (Selector Field obj st ctx r) ctx' = ctx'
type instance QueryObject (Selector Field obj st ctx r) = r
type instance QueryResult (Selector Field obj st ctx r) Method st' m x =
Selector Method obj st ctx x
type instance QueryResult (Selector Field obj st ctx r) Field st' m x =
Selector Field obj st ctx x
instance (Object obj st ctx, Object r st' Identity, m ~ Identity) =>
Object (Selector Field obj st ctx r) st' m where
-- get the value of the field on the LHS, run the RHS method on it, then
-- set the value of the field to the object returned by the RHS method
(.!) (MkField eg ig es is) (MkMethod ri re) = MkMethod
(do obj <- ig
-- * obj is the value (an object) of the field on the left
-- * use obj as object for the method on the right
let (r,obj') = runIdentity (re obj)
-- * update the current object with obj', the updated obj
is obj'
-- * return the result of the method on the right
return r)
(\this -> do (obj,this') <- eg this
-- * use obj as object for the method on the right
let (r,obj') = runIdentity (re obj)
-- * update the current object with s, the updated r
(_,this'') <- es this' obj'
return (r,this''))
(.!) (MkField eg ig es is) (MkField reg rig res ris) = MkField
(\this -> do (obj,this') <- eg this
let (r,obj') = runIdentity (reg obj)
(_,this'') <- es this' obj'
return (r,this''))
(do obj <- ig
let (r,obj') = runIdentity (reg obj)
is obj'
return r)
(\this v -> do (obj,this') <- eg this
let (r,obj') = runIdentity (res obj v)
(_,this'') <- es this' obj'
return (r,this''))
(\v -> do obj <- ig
let (r,obj') = runIdentity (res obj v)
is obj'
return r)
-- For `this':
-- * run the internal call (if the selector on the RHS is a method)
-- * run the internal getter (if the selector on the RHS is a field)
type instance QueryMonad (This obj st ctx r) ctx' = ctx'
type instance QueryObject (This obj st ctx r) = obj
type instance QueryResult (This obj st ctx r) ty st' m x = StateT st' ctx x
instance (Object obj st ctx, Object r st' ctx', ctx ~ ctx', st ~ st') =>
Object (This obj st ctx r) st' ctx' where
(.!) _ (MkMethod ri re) = ri
(.!) _ (MkField ge gi se si) = gi