4

As part of a mini interpreter that I'm writing in Haskell, I'm writing a function that does the following: In case of eval (App e1 e2), I want to recursively evaluate e1 (eval e1), setting the result to v1. Then using Switch/Case, I want to check the pattern of the v1 and if it's not an Error then recursively evaluate e2 (eval e2) and setting that value to v2. Using these two values v1 and v2, then I apply another function (appVals) on those values.

eval :: Exp -> Error Val
eval (App e1 e2) = appVals v1 v2 where
    v1 = case (eval e1) of
        Error err -> Error "Not an application"
        /= Error err -> eval e1 = v1
    v2 = case (eval e2) of
        Error err -> Error "Not an application"
        /= Error err -> eval e2 = v2

I think I may have figured it out but I'm not entirely sure I've done the switch/case part correctly. Any ideas/suggests?

4

4 回答 4

12

您的案例陈述的第二部分不应该尝试重新测试,因为您已经知道它不是Error- 与第一部分相匹配。(跳过/= Error err。)

eval e1 = v1尝试重做你在开始时所做的评估。你不需要这样做。

这就是我认为你打算做的事情:

eval :: Exp -> Error Val
eval (App e1 e2) = case eval e1 of
    Error _ -> Error "Not an application"
    S v1    ->    case eval e2 of        -- nested case statement
     Error _ -> Error "Not an application"
     S v2    -> appVals v1 v2            -- match the S away

但这一切似乎有点难看,所以让我们从 Gabriel Gonzalez 那里得到极好的建议,并从Error.

instance Functor Error where
   fmap f (Error e) = Error e  -- pass through errors
   fmap f (S x)     = S (f x)  -- edit successes

例如,fmap (+4) (Error "oops") = Error "oops"fmap (+4) (S 5) = S 9.

如果这fmap对您来说是全新的,为什么不阅读Functors 教程呢?

接下来让我们创建一个 Applicative 实例。Applicative 让您可以像使用简单的功能一样使用复杂的功能。您需要import Control.Applicative在文件顶部才能使其正常工作。

instance Applicative Error where
    pure x = S x   -- how to put ordinary data in
    S f     <*> S x     = S (f x)
    Error e <*> _       = Error e
    _       <*> Error e = Error e

现在,如果没有任何错误,那么您将定义

appVal' :: Val -> Val -> Val

eval' :: Exp -> Val
eval' (App e1 e2) = appVal' (eval' e1) (eval' e2)

使用 applicative,我们可以使用<$>which 有点像,$除了它执行您在fmap. 类似地,<*>工作有点像函数应用程序,除了额外的管道,所以我们可以定义

eval :: Exp -> Error Val
eval (App e1 e2) = appVals <$> eval e1 <*> eval e2

这是处理幕后错误同时专注于功能的一种很好的干净方式。

于 2012-11-18T21:40:30.333 回答
4

我昨天离开了你(模重命名)

eval (App e1 e2) = appVals <$> eval e1 <*> eval e2

这几乎就是您想要的,不同之处在于如果两个输入中的任何一个是错误的,您想用特定的错误消息替换它。(我不明白为什么。)所以让我们编写一个函数来完成最后一点!

butError :: String -> Error a -> Error a
butError message (Error _) = Error message
butError _       noError   = noError

现在您可以将您的eval子句重写为

eval (App e1 e2) = appVals <$> butError message (eval e1) <*> butError message (eval e2)
  where message = "Not an application"
于 2012-11-18T23:36:25.073 回答
3

我假设您有一些数据类型,如下所示:

data Result a = Error String | Success a

您的问题的解决方案是:

(a) 使你的Result类型 aMonad

(b) 使用Either String而不是结果,因为它已经有一个Monad实例。

无论你做什么,你都会写:

eval :: SyntaxTree a -> Result a
eval (App ef ex) = do
    f <- eval ef
    x <- eval ex
    return (f x)

从技术上讲,一个Applicative就足够了,你可以写:

eval (App ef ex) = eval ef <*> eval ex
于 2012-11-18T22:03:17.650 回答
2

在 acase中,你需要有替代模式,你不能在/= Error err那里。

此外,表达式中的箭头右侧不能有 a,因此不是=有效的Haskell。->case-> eval e1 = v1

在您的情况下,您只关心它不是一个Error something,但是一旦第一个模式不匹配,就会自动出现这种情况,并且由于您不在乎得到什么,您可以使用变量模式:

eval (App e1 e2) = appVals v1 v2 where
    v1 = case (eval e1) of
        Error err -> Error "Not an application"
        other -> other
    v2 = case (eval e2) of
        Error err -> Error "Not an application"
        other -> other
于 2012-11-18T21:40:55.910 回答