2016-05-30 4 views
1

Для проекта нам было поручено написать императивный язык и выполнить его через Haskell. Парсер (опущен здесь) и части оценки выполнены. Теперь остается только закодировать эффекты. Манипулирование маленьким роботом.Выполнение операции ввода-вывода, но возврат другого типа

Учитывая следующий код:

data Env = Env [Binding] 
instance Show Env where 
    show (Env (x:xs)) = show x ++ ", " ++ show (Env xs) 
    show (Env []) = "" 

data Binding = Binding (String,Int) 
instance Show Binding where 
    show (Binding x) = fst x ++ " : " ++ show (snd x) 

lookup' :: String -> Env -> Int 
lookup' zoek (Env env) = case elemIndex zoek [fst x | Binding x <- env] of 
    Just y -> y 
    Nothing -> error "Not found" 


eval :: Stmt -> Env -> Env 
eval (Seq s) env = foldl (flip eval) env s 
eval (Assign varName aexpr) env = evalAssign varName aexpr env 
eval (If bool stmt1 stmt2) env = evalIf bool stmt1 stmt2 env 
eval (While bool stmt) env = undefined 
eval (MotorInstruct string aExpr) env = undefined 
eval (SensorRead string) env = undefined 
eval Skip env = env 

evalAExpr :: AExpr -> Env -> Int 
evalAExpr (IntConst int) _ = fromInteger int 
evalAExpr (Neg a) env = - evalAExpr a env 
evalAExpr (ABinary Add a b) env = evalAExpr a env + evalAExpr b env 
evalAExpr (ABinary Subtract a b) env = evalAExpr a env - evalAExpr b env 
evalAExpr (ABinary Multiply a b) env = evalAExpr a env * evalAExpr b env 
evalAExpr (ABinary Divide a b) env = evalAExpr a env `div` evalAExpr b env 
evalAExpr (Var x) env = getElementAtEnv env (lookup' x env) 
    where 
    getElementAtEnv (Env env) index = getSndFromBinding (env !! index) 
    getSndFromBinding (Binding (_,t)) = t 


evalBExpr :: BExpr -> Env -> Bool 
evalBExpr (BoolConst bool) _ = bool 
evalBExpr (Not expr) env = not $ evalBExpr expr env 
-- Boolean operators 
evalBExpr (BBinary And a b) env = evalBExpr a env && evalBExpr b env 
evalBExpr (BBinary Or a b) env = evalBExpr a env || evalBExpr b env 
-- Relational operators 
evalBExpr (RBinary Greater a b) env = evalAExpr a env > evalAExpr b env 
evalBExpr (RBinary Less a b) env = evalAExpr a env < evalAExpr b env 
evalBExpr (RBinary Equal a b) env = evalAExpr a env == evalAExpr b env 


evalIf :: BExpr -> Stmt -> Stmt -> Env -> Env 
evalIf expr s1 s2 env = if evalBExpr expr env 
    then 
    eval s1 env 
    else 
    eval s2 env 


evalAssign :: String -> AExpr -> Env -> Env 
evalAssign term s (Env env)= if term `elem` transform 
    then 
    Env (take (lookup' term (Env env)) env ++ [Binding (term, evalAExpr s (Env env))]++ drop (lookup' term (Env env) + 1) env) 
    else 
    Env (env ++ [Binding (term, evalAExpr s (Env env))]) 
    where transform = [ fst ele | Binding ele <- env] 


zoekMotor :: String -> Int 
zoekMotor "left" = 0x9 
zoekMotor "right" = 0xa 
zoekMotor _ = error "No such motor" 


sendToMotor :: String -> Int -> IO() 
sendToMotor m s = do 
    bot <- openMBot 
    sendCommand bot $ setMotor (zoekMotor m) s s 
    closeMBot bot 

evalMotorInstruct :: String -> AExpr -> Env -> Env 
evalMotorInstruct welke waarde env = do 
    sendToMotor welke (evalAExpr waarde env) 
    return env 

Как бы я идти о выполнении функции sendToMotor (которая возвращает IO()) в моей функции оценивая evalMotorInstruct, которая должна возвращать Env? Я немного растерялся в том, как я выполнил бы свою «действие» - функцию и только верну свое Env из функции оценки.

Обратите внимание, что текущий код для evalMotorInstruct недействителен. Функция должна возвращать Env, но на самом деле возвращает IO Env

Спасибо

+3

Вы обнаружили одно из интересных свойств Haskell - его чистоту. Вы не можете выполнить операцию ввода-вывода и вернуть результат * не *, завернутый в IO. Это связано с тем, что функции Haskell не могут выполнять произвольные побочные эффекты; они должны быть чистыми. Вы * можете * вернуть 'IO Env', который, кажется, именно то, что вы ищете здесь, основываясь на использовании' do' и 'return'. –

+0

@AlexisKing Означает ли это, что я должен преобразовать все мои другие функции «eval», чтобы вернуть IO Env? И «разворачивать» их в любой ситуации? – MrKickkiller

+1

Возможно, вы захотите изучить свободную монаду, чтобы избежать фиксации 'IO'. http://www.haskellforall.com/2012/06/you-could-have-invented-free-monads.html – user2297560

ответ

2

Ваши AExpr и BExpr типов представляют собой чистые вычисления на вашем языке - они не только не делать какой-либо операции ввода-вывода, но они также дон Не изменяйте среду. Следовательно, вам не придется изменять свои функции eval.

Так что вам просто нужно изменить оценку значений Stmt. Тип подписи изменится:

eval :: Stmt -> Env -> IO Env 

Пример того, как eval Seq изменится:

eval (Seq []) env  = return env 
eval (Seq (s:ss)) env = do env' <- eval s env -- eval the first statement 
          eval (Seq ss) env' -- eval the rest 

Обратите внимание, что eval If не нужно менять:

eval (If bool stmt1 stmt2) env = 
    if evalBExpr bool env 
    then eval stmt1 env 
    else eval stmt2 env 

evalMotorInstruct компилируется, если вы меняете его подпись на:

evalMotorInstruct :: String -> AExpr -> Env -> IO Env 

Я оставлю все остальное вам.

При рефакторинге просто закомментируйте код, который не компилируется. Затем инкрементно добавляйте строки обратно в один за другим, чтобы их компилировать перед добавлением другой строки. Используйте ... = undefined, если вам нужно. Вернитесь и заполните их позже.

+0

Потенциальным преимуществом по сравнению с этим было бы использование «StateT Env IO» в качестве монады, что позволило бы вам пропустить передачу «Env» вручную, а также вы могли бы написать «eval (Seq ss) = mapM_ eval ss' и т. Д. , – Cactus

Смежные вопросы