我正在通过为comm语言实现一个简单的解释器来学习如何在 Haskell 中使用 Arrows。
我有一个 eval 函数,它将表达式计算为一个值,但是当输入任何表达式时,eval 会无限循环。
-- Interp.hs
eval :: A Expr Val
eval = proc e -> case e of
Lit x -> returnA -< Num x
Var s -> do
lookup -< s
Add e1 e2 -> do
v1 <- eval -< e1
v2 <- eval -< e2
case (v1, v2) of
(Num x, Num y) -> returnA -< Num (x + y)
在 GHCI 中执行此操作会导致无限循环
*Interp> unpack eval M.empty (Lit 1)
在 Add 表达式的情况下注释掉 eval 确实会导致表达式给出结果
例如
-- Interp.hs
eval :: A Expr Val
eval = proc e -> case e of
Lit x -> returnA -< Num x
Var s -> do
lookup -< s
Add e1 e2 -> do
returnA -< Num 1
-- v1 <- eval -< e1
-- v2 <- eval -< e2
-- case (v1, v2) of
-- (Num x, Num y) -> returnA -< Num (x + y)
*Interp> unpack eval M.empty (Lit 1)
(Right (Num 1),fromList [])
这是有问题的代码
使用的箭头是一种状态函数,它在失败后继续传递上下文。
{-# LANGUAGE Arrows #-}
{-# OPTIONS_GHC -Wall #-}
module Interp where
import Prelude hiding (lookup, fail)
import qualified Data.Map as M
import Control.Arrow
import Control.Category
data Expr
= Lit Int
| Var String
| Add Expr Expr
deriving (Show, Eq)
data Val
= Num Int
deriving (Show, Eq)
type Env = M.Map String Val
data A b c = A { unpack :: (Env -> b -> (Either String c, Env)) }
instance Category A where
id = A (\env b -> (Right b, env))
A g . A f = A $ \env b -> case f env b of
(Left err, env') -> (Left err, env')
(Right c, env') -> g env' c
instance Arrow A where
arr f = A $ \env b -> (Right (f b), env)
first (A f) = A $ \env (b, d) -> case f env b of
(Left err, env') -> (Left err, env')
(Right c, env') -> (Right (c, d), env')
instance ArrowChoice A where
left (A f) = A $ \env e -> case e of
Left b -> case f env b of
(Left err, env') -> (Left err, env')
(Right c, env') -> (Right (Left c), env')
Right d -> (Right (Right d), env)
lookup :: A String Val
lookup = A $ \env k -> case M.lookup k env of
Nothing -> (Left "Variable not bound", env)
Just v -> (Right v, env)
eval :: A Expr Val
eval = proc e -> case e of
Lit x -> returnA -< Num x
Var s -> do
lookup -< s
Add e1 e2 -> do
v1 <- eval -< e1
v2 <- eval -< e2
case (v1, v2) of
(Num x, Num y) -> returnA -< Num (x + y)