2014-05-17 21 views
5

Haskell'de yazılmış ilkel tercümanım var. Bu yorumlayıcı return ifadelerini (see my previous question) doğru şekilde işleyebilir.Süreklilik ve durum monad transformatörler oluşturmak için doğru yolu

Şimdi global durumu çevirmenime eklemek istiyorum. Bu durum, global koddan veya işlev kodu 'dan (runCont ile return mantığını sağlamak için çalıştırılır) değiştirilebilir.

import Control.Monad.Cont 
import Control.Monad.State 

type MyState = String 
data Statement = Return Int | GetState | SetState MyState | FuncCall [Statement] deriving (Show) 
data Value = Undefined | Value Int | StateValue MyState deriving (Show) 

type Eval a = StateT MyState (Cont (Value, MyState)) a 

runEval ::(Eval Value) -> MyState -> (Value, MyState) 
runEval eval state = runCont (runStateT eval state) id 

evalProg :: [Statement] -> Value 
evalProg stmts = fst $ runEval (evalBlock stmts) $ "" 

evalBlock :: [Statement] -> Eval Value 
evalBlock [] = return Undefined 
evalBlock [stmt] = evalStatment stmt 
evalBlock (st:stmts) = evalStatment st >> evalBlock stmts 

evalStatment :: Statement -> Eval Value 
evalStatment (Return val) = do 
    state <- get 
    lift $ cont $ \_ -> (Value val, state) 
evalStatment (SetState state) = put state >> return Undefined 
evalStatment (FuncCall stmts) = do 
    -- I don't like this peace of code 
    state <- get 
    (value, newState) <- return $ runEval (evalBlock stmts) $ state 
    put newState 
    return value 
evalStatment GetState = get >>= return . StateValue 

test2 = evalProg [SetState "Hello", FuncCall [SetState "Galaxy", Return 3], GetState] -- result is StateValue "Galaxy" 

Bu kod çalışıyor ama bu kodun evalStatment (FuncCall stmts) bölümünü sevmiyorum:

kod burada sunulmuştur. Şu anki çevirmen durumunu runEval işlevine, 'a iletiyorum ve sonra değiştirilmiş duruma geri dönün ve yeni çevirmen durumu olarak ayarlayın.

Bu kodu iyileştirmek mümkün müdür? Bir şekilde fonksiyonun kodunu (FuncCall) yorumlayıcının durumu üzerinde çalışabilir miyim (geçerli durum ve çalışma fonksiyonunun kodunu almadan ve tercümanın yeni durumunu açıkça belirten)?

+0

Sadece diziye ihtiyacınız var mı? :: Monad m => [m a] -> m [a] '? Muhtemelen 'liftM last :: Monad m => m [a] -> m a' tarafından takip ediliyor? –

cevap

4

Sana

type Eval a = ContT Value (State MyState) a 

Bu şekilde temel Monad değiştirmek önermek, State MyState parçası "monad trafo yığının" alt kısmında ve daha kolay sadece üst çekip almak mümkün olacak devleti etkilemeden devam parçası. Sonra FuncCall durum basitçe

evalStatment (FuncCall stmts) = lift $ runContT (evalBlock stmts) return 

Tabii bu aynı zamanda diğer bazı kısımlarını yeniden gerektirecektir olabilir. Ama fazla değil ve çoğu aslında daha da kolaylaşıyor! Değiştirmek için gereken tüm parçalar:

type Eval a = ContT Value (State MyState) a 

runEval eval state = runState (runContT eval return) state 

evalStatment (Return val) = ContT $ \_ -> return (Value val) 

evalStatment (FuncCall stmts) = lift $ runContT (evalBlock stmts) return 
+0

teşekkürler, aradığım şey bu! – sergeyz

İlgili konular