Mise à jour: J'ai ajouté an answer qui décrit ma solution finale (indice: le type de données Expr
n'était pas suffisant).Evaluateur mutuellement récursif dans Haskell
Je suis writing un évaluateur pour une langue peu d'expression, mais je suis coincé sur la construction LetRec
.
C'est la langue:
type Var = String
type Binds = [(Var, Expr)]
data Expr
= Var Var
| Lam Var Expr
| App Expr Expr
| Con Int
| Sub Expr Expr
| If Expr Expr Expr
| Let Var Expr Expr
| LetRec Binds Expr
deriving (Show, Eq)
Et ce ce l'évaluateur jusqu'à présent:
data Value
= ValInt Int
| ValFun Env Var Expr
deriving (Show, Eq)
type Env = [(Var, Value)]
eval :: Env -> Expr -> Either String Value
eval env (Var x) = maybe (throwError $ x ++ " not found")
return
(lookup x env)
eval env (Lam x e) = return $ ValFun env x e
eval env (App e1 e2) = do
v1 <- eval env e1
v2 <- eval env e2
case v1 of
ValFun env1 x e -> eval ((x, v2):env1) e
_ -> throwError "First arg to App not a function"
eval _ (Con x) = return $ ValInt x
eval env (Sub e1 e2) = do
v1 <- eval env e1
v2 <- eval env e2
case (v1, v2) of
(ValInt x, ValInt y) -> return $ ValInt (x - y)
_ -> throwError "Both args to Sub must be ints"
eval env (If p t f) = do
v1 <- eval env p
case v1 of
ValInt x -> if x /= 0
then eval env t
else eval env f
_ -> throwError "First arg of If must be an int"
eval env (Let x e1 e2) = do
v1 <- eval env e1
eval ((x, v1):env) e2
eval env (LetRec bs e) = do
env' <- evalBinds
eval env' e
where
evalBinds = mfix $ \env' -> do
env'' <- mapM (\(x, e') -> eval env' e' >>= \v -> return (x, v)) bs
return $ nub (env'' ++ env)
C'est ma fonction de test que je veux évaluer:
test3 :: Expr
test3 = LetRec [ ("even", Lam "x" (If (Var "x")
(Var "odd" `App` (Var "x" `Sub` Con 1))
(Con 1)
))
, ("odd", Lam "x" (If (Var "x")
(Var "even" `App` (Var "x" `Sub` Con 1))
(Con 0)
))
]
(Var "even" `App` Con 5)
MODIFIER:
Basé sur la réponse de Travis et le commentaire de Luke, j'ai mis à jour mon code pour utiliser l'instance MonadFix pour la monade d'erreur. L'exemple précédent fonctionne bien maintenant! Cependant, l'exemple ci-dessous ne fonctionne pas correctement:
test4 :: Expr
test4 = LetRec [ ("x", Con 3)
, ("y", Var "x")
]
(Con 0)
Lors de l'évaluation cela, l'évaluateur des boucles, et rien ne se passe. Je suppose que j'ai fait quelque chose d'un peu trop strict ici, mais je ne suis pas sûr de ce que c'est. Est-ce que je viole l'une des lois MonadFix?
Avez-vous essayé d'enlever le nub? –
@Sjoerd Oui, j'ai essayé de supprimer 'nub', car dans cet exemple il n'y a pas de doublons de toute façon. Ça ne marche toujours pas. –