packages feed

lambdabot-4.0: Plugin/Lambda/LambdaTerm.hs

module Plugin.Lambda.LambdaTerm where

import Plugin.Lambda.LangPack

import Data.Map (Map)
import qualified Data.Map as Map hiding (Map)

import Control.Monad.Error
import Control.Monad.State
import Control.Monad.Reader
import Control.Monad.Cont

import Data.Dynamic

type Fun m = (m Dynamic -> m Dynamic)

data LambdaTerm term
   = Lam !String term
   | App term term
   | Var !String

type IState m = (Int,Map Int (m Dynamic),Int) -- record instead of tuple would be more robust
type Ref = Int

prjF :: (Typeable (m Dynamic), MonadError String m) => m Dynamic -> m (Fun m)
prjF = prj'

phiLambda :: (MonadError String m,
              MonadState (IState m) m,
              Typeable (m Dynamic),
              Pause m (Either Dynamic b),
              Typeable b,
              MonadReader (Map String (m Dynamic)) m) =>
              LambdaTerm (m Dynamic) -> m Dynamic
phiLambda (Var v) = do step; lookupEnv v
{-
phiLambda (Lam v b) = do env <- ask
                         return $ inj (\v' -> do loc <- newRef undefined
                                                 let thunk =
                                                       do r <- v'
                                                          writeRef loc
                                                                   (return r)
                                                          return r
                                                 writeRef loc thunk
                                                 let env' = addToFM env v (readRef loc)
                                                 inEnv env' b)
-}
phiLambda (Lam v b) = do env <- ask
                         return $ inj (\v' -> do rr <- thunkify v'
                                                 inEnv (Map.insert v rr env) b)
phiLambda (App f x) = do f' <- prjF f; env <- ask; f' (inEnv env x)

thunkify :: (MonadState (IState m) m) => m Dynamic -> m (m Dynamic)
thunkify c = do
    loc <- newRef (error "empty reference")
    let thunk = do r <- c
                   writeRef loc (return r)
                   return r
    writeRef loc thunk
    return (readRef loc)

lookupEnv :: (MonadReader (Map String (m Dynamic)) m,
              Typeable (m Dynamic),
              MonadError String m) => String -> m Dynamic
lookupEnv k = do env <- ask
                 let v = Map.lookup k env 
                 maybe (throwError ("unbound variable: "++k)) id v

inEnv :: (MonadReader (Map String (m Dynamic)) m) =>
      Map String (m Dynamic) -> m Dynamic -> m Dynamic
inEnv env b = local (const env) b

step :: (MonadState (IState m) m, MonadError String m,
        Typeable b,
        Pause m (Either Dynamic b)) => m ()
step = do (c,hp,fuel) <- get
          if fuel == 0 then do refuel <- pause $ Left . inj
                               put (c,hp,refuel)
                       else put (c,hp,fuel-1)

newRef :: (MonadState (IState m) m) => m Dynamic -> m Ref
newRef v = do (c,hp,fuel) <- get
              put (c+1,Map.insert c v hp,fuel)
              return c

readRef :: (MonadState (IState m) m) => Ref -> m Dynamic
readRef loc = do (_,hp,_) <- get; let {Just x = Map.lookup loc hp}; x

writeRef :: (MonadState (IState m) m) => Ref -> m Dynamic -> m ()
writeRef loc v = do (c,hp,fuel) <- get
                    put (c,Map.insert loc v hp,fuel)

instance Functor LambdaTerm where
    fmap _ (Var v) = Var v
    fmap f (Lam v b) = Lam v (f b)
    fmap f (App g x) = App (f g) (f x)