packages feed

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