packages feed

tempus-0.1.0: Tempus/Evaluation.hs

-- | Evaluation of Tempus expressions.
module Tempus.Evaluation (
    EvalEnv,
    Value (..),

    evalExpr
) where


import Tempus.Syntax
import Tempus.TypeCheck

import Data.List

-- | Representation of evaluation environments as a list of pairs of a variable with its expression.
type EvalEnv = [(Var, Expr)]

-- | Internal format for the value of an expression.
data Value = Natural Integer
           | Unit
           | Function (Value -> Value)
           | Pair Value Value
           | ChoiceLeft Value
           | ChoiceRight Value
           | Behavior (RelTime -> Value)
           | Event RelTime Value

-- | Number of time steps with corresponding values to include when showing a bahavior.
behaviorShowFuture :: Integer
behaviorShowFuture = 7

-- | Number of time steps to look ahead when deciding if to show an event as never occuring or not.
eventShowLookAhead :: Integer
eventShowLookAhead = 100

-- TODO: make length of behavior future scalable
-- TODO: make mu/nu expression depth scalable
instance Show Value where
    showsPrec _ (Natural i) = shows i
    showsPrec _ (Unit) = showString "()"
    showsPrec _ (Function _) = showString "<function>"
    showsPrec p (Pair e1 e2) = showParen (p > 0) $
        showsPrec 1 e1 . showString " , " . showsPrec 0 e2
    showsPrec p (ChoiceLeft e) = showParen (p > 3) $
        showString "left " . showsPrec 4 e
    showsPrec p (ChoiceRight e) = showParen (p > 3) $
        showString "right " . showsPrec 4 e
    showsPrec p (Behavior f) = showParen (True) $ showString $ (
        concat . intersperse "; "
        . map (\ps -> case ps of
                          [(v, t)] -> "@" ++ show (fromTime t) ++ ": " ++ v
                          ps -> "@" ++ (let ps' = map snd ps in
                                    show (head ps') ++ ".." ++ show (last ps')) ++ ": " ++ fst (head ps))
        . groupBy (\x y -> fst x == fst y) . map (\t -> (show $ f t, t)) $
              map fromInteger [1..behaviorShowFuture]) ++ "; ..."
    showsPrec p (Event t e) = showParen (p > 3) $
        if t > fromInteger eventShowLookAhead
            then showString "?"
            else showString "@" . shows t . showString ": " . shows e

-- | Time representation for events and behaviors
data RelTime = One
             | Succ RelTime
             deriving (Eq)

-- | A 'RelTime' value for an infinite number of time steps.
infinite :: RelTime
infinite = Succ infinite

instance Show RelTime where
    show t = show $ fromTime t

-- | Conversion from 'RelTime' values to 'Integer' values
fromTime :: RelTime -> Integer
fromTime One      = 1
fromTime (Succ t) = 1 + fromTime t

instance Ord RelTime where
    compare One       One       = EQ
    compare One       _         = LT
    compare (Succ t1) (Succ t2) = compare t1 t2
    compare (Succ _)  _         = GT

    min One      t'        = One
    min t        One       = One
    min (Succ t) (Succ t') = Succ $ min t t'

instance Num RelTime where
    t + t' = Succ $ case t of
                        One    -> t'
                        Succ t -> t + t'

    Succ t - One     = t
    Succ t - Succ t' = t - t'
    _      - _       = error "undefined value for RelTime"

    fromInteger = genericIndex (iterate Succ One) . pred

    (*) = undefined
    abs = undefined
    signum = undefined

{- |
    @evalExpr sEnv tEnv eEnv e@ evaluates an expression @e@ given 

        [@sEnv@] the list of type synonyms,

        [@tEnv@] the list of global variables with their types, and

        [@eEnv@] the list of global variables with their expressions.
-}
evalExpr :: TypeSynEnv -> TypeEnv -> EvalEnv -> Expr -> Value
evalExpr = eval []

{- |
    Helper function for 'evalExpr'. @eval lEnv sEnv tEnv eEnv e@ evaluates an expression @e@
    given

        [@lEnv@] the list of local variables with their values,

        [@sEnv@] the list of type synonyms,

        [@tEnv@] the list of global variables with their types, and

        [@eEnv@] the list of global variables with their expressions.
-}
eval :: [(Var, Value)] -> TypeSynEnv -> TypeEnv -> EvalEnv -> Expr -> Value
eval lEnv sEnv tEnv eEnv expr = let eval' = eval lEnv sEnv tEnv eEnv in case expr of
    ExPair e1 e2 -> Pair (eval' e1) (eval' e2)
    ExNatLit i -> Natural i
    ExUnit -> Unit
    ExLeft -> Function $ \v -> ChoiceLeft v
    ExRight -> Function $ \v -> ChoiceRight v
    ExNull -> Function $ \_ -> error "eval: received value of type 0"
    ExFst -> Function $ \p -> case p of
                                  Pair l _ -> l
                                  _ -> error "eval: argument to ExFst not a pair"
    ExSnd -> Function $ \p -> case p of
                                  Pair _ r -> r
                                  _ -> error "eval: argument to ExSnd not a pair"
    ExCase -> Function $ \(Function l) -> Function $ \(Function r) -> Function $ \c -> case c of
        ChoiceLeft e -> l e
        ChoiceRight e -> r e
        _ -> error "third argument to case not left/right"

    ExVar (Var "add") -> withNat $ \i -> withNat $ \j -> Natural $ i + j
    ExVar (Var "mult") -> withNat $ \i -> withNat $ \j -> Natural $ i * j
    ExVar v -> case lookup v lEnv of
                   Just x -> x
                   Nothing -> maybe (error $ "eval: undefined var `" ++ show (SrcCode v) ++ "'")
                                  eval' $ lookup v eEnv

    ExLam v e -> Function (\x -> eval (replVar v x lEnv) sEnv tEnv eEnv e)
    ExApp f x -> case eval' f of
                     Function fun -> fun $ eval' x
                     _ -> error "eval: first argument to ExApp not a function"

    ExBehav f -> case eval' f of
                     Function f -> Behavior (f . Natural . fromTime)
                     _ -> error "first argument to ExBehav not a function"
    ExEvent t e -> case eval' t of
                       Natural i -> Event (fromInteger i) $ eval' e
                       _ -> error "first argument to ExEvent not a positive"
    ExNever -> Event infinite $ error "secret value of type 0"
    ExConst e -> Behavior $ \_ -> eval' e
    ExLiftAppB f b -> case (eval' f, eval' b) of
        (Behavior f, Behavior g) -> Behavior $ \t -> case f t of
            Function f -> f (g t)
            _ -> error "wrong argument type(s) to ExLiftAppB"
        _ -> error "wrong argument type(s) to ExLiftAppB"
    ExLiftAppE f e -> case (eval' f, eval' e) of
        (Behavior f, Event t v) -> Event t $ case f t of
            Function f -> f v
            _ -> error "wrong argument type(s) to ExLiftAppE"
        _ -> error "wrong argument type(s) to ExLiftAppE"
    ExRace -> Function $ \e1 -> case e1 of
        Event t1 v1 -> Function $ \e2 -> case e2 of
            Event t2 v2 -> Event (min t1 t2) $ case compare t1 t2 of
                LT -> ChoiceRight $ ChoiceLeft $ Pair v1 (Event (t2 - t1) v2)
                EQ -> ChoiceLeft $ Pair v1 v2
                GT -> ChoiceRight $ ChoiceRight $ Pair v2 (Event (t1 - t2) v1)
            _ -> error "second argument to ExRace not an event"
        _ -> error "first argument to ExRace not an event"
    ExExpand -> Function $ \e -> case e of
        Behavior f -> Behavior $ \t -> Pair (f t) (Behavior $ \t' -> f $ t + t')
        _ -> error "first argument to ExExpand not a behavior"
    ExPack _ -> Function id
    ExUnpack _ -> Function id
    ExReflect -> Function $ \v -> let nat 1 = ChoiceLeft Unit
                                      nat n = ChoiceRight $ nat (n-1)
                                  in case v of
                                         Natural n -> nat n
                                         _ -> error "eval: argument to ExReflect not a positive"

    -- fold :: (Functor shape) => (shape accu -> accu) -> Fix shape -> accu
    -- fold fun = fun . fmap (fold fun) . unFix
    ExFold mu f ->
        let Right (MuType var t) = expandMuType sEnv mu
            fold fun = fun . genmap CoVariant sEnv var t (fold fun)
        in case eval' f of
               Function fun -> Function $ fold fun
               _ -> error "eval: first argument to ExFold not a function"

    -- unfold :: (Functor shape) => (accu -> shape accu) -> accu -> Fix shape
    -- unfold fun = Fix . fmap (unfold fun) . fun
    ExUnfold nu f ->
        let Right (NuType var t) = expandNuType sEnv nu
            unfold fun = genmap CoVariant sEnv var t (unfold fun) . fun
        in case eval' f of
               Function fun -> Function $ unfold fun
               _ -> error "eval: first argument to ExUnfold not a function"

    ExUJump ->
        let jump e = case e of
                Event t v ->
                    let (t', v') = case v of
                                       ChoiceLeft v'  -> (One, v')
                                       ChoiceRight e' -> jump e'
                                       _ -> error "eval: inner argument to ExUJump not a choice"
                    in (t + t', v')
                _ -> error "eval: argument to ExUJump not an event"
        in Function $ \e -> let (Succ t, v) = jump e in Event t v

    ExUSwitch ->
        let switch p = case p of
                Pair (Behavior f) (Event t ~(Pair v p')) -> \t' -> case compare t' t of
                    LT -> f t'
                    EQ -> v
                    GT -> switch p' (t' - t)
                _ -> error "wrong argument type(s) to ExUSwitch"
        in Function $ \p -> let f = switch p in Behavior f


{- |
    @genmap var sEnv v t f@ generates a generic comap\/contramap function for a type @t@ that
    applies a function @f@ to values of a type @t@. If @var == CoVariant@ a comap function will be
    produced, a contramap function for @var == ContraVariant@, where @v@ is the type variable the
    map is generated for and @sEnv@ the list of type synonyms.
-}
genmap :: Variance -> TypeSynEnv -> Var -> Type -> (Value -> Value) -> Value -> Value
genmap var sEnv v t f = case t of
    TyZero -> id
    TyUnit -> id
    TyNat -> id

    TyBehav t -> \e -> case e of
        Behavior g -> Behavior $ genmap var sEnv v t f . g
        _ -> error "genmap: value of type TyBehav is not a behavior"
        
    TyEvent t -> \e -> case e of
        Event n e' -> Event n $ genmap var sEnv v t f e'
        _ -> error "genmap: value of type TyBehav is not a behavior"

    TyPair t1 t2 -> \e -> case e of
        Pair e1 e2 -> Pair (genmap var sEnv v t1 f e1) (genmap var sEnv v t2 f e2)
        _ -> error "genmap: value of type TyPair is not a pair"

    TyPlus t1 t2 -> \e -> case e of
        ChoiceLeft e'  -> ChoiceLeft $ genmap var sEnv v t1 f e'
        ChoiceRight e' -> ChoiceRight $ genmap var sEnv v t2 f e'
        _ -> error "genmap: value of type TyPlus is not a choice"

    TyFun t1 t2 -> \e -> case e of
{-
        -- comap f function = comap f . function . contramap f
        (Function g, CoVariant) -> Function $
            genmap CoVariant sEnv rEnv t2 . g . genmap ContraVariant sEnv rEnv t1
        -- contramap f function = contramap f . function . comap f
        (Function g, ContraVariant) -> Function $
            genmap ContraVariant sEnv rEnv t2 . g . genmap CoVariant sEnv rEnv t1
-}
        Function g -> Function $ genmap var sEnv v t2 f
                                 . g . genmap (invertVariance var) sEnv v t1 f
        _ -> error "genmap: value of type TyFun not a function"

    TyApp v' [] | v' == v -> f
    TyApp v' ts -> case expandTypeSyn sEnv v' ts of
                       Left _  -> id
                       Right t -> genmap var sEnv v t f

    TyMu (MuType v' t') ->
        -- TODO: TyUnit here assures we get the subtrees as they are. That is a type error but 
        --       shouldn't be a problem for evaluation (only if v is mapped to a type including v
        --       itself, which shouldn't happen). Should be thoroughly thought through, though :-)
        let temp = TyUnit
        in genmap var sEnv v (substVar v' temp t') f
           . genmap var sEnv v' t' (genmap var sEnv v t f)

    TyNu (NuType v' t') ->
        -- TODO: see above
        let temp = TyUnit
        in genmap var sEnv v (substVar v' temp t') f
           . genmap var sEnv v' t' (genmap var sEnv v t f)

    TyVar _ -> error "genmap: called with TyVar"
    TyCon _ -> error "genmap: called with TyCon"


withNat :: (Integer -> Value) -> Value
withNat f = Function $ \x -> case x of
    Natural n -> f n
    _ -> error "eval: function argument type mismatches expected type positive"

-- | @replVar v val@ adds a new variable @v@ with a value @var@ to a list of variable-value-pairs,
-- replacing the old value of @v@ if @v@ is already present.
replVar v v' = ((v, v') :) . filter ((/= v) . fst)