Haskell开关/案例使用

时间:2012-11-18 21:36:12

标签: haskell recursion switch-statement

作为我在Haskell编写的迷你解释器的一部分,我正在编写一个执行以下操作的函数:如果是eval (App e1 e2),我想递归计算e1({{ 1}}),将结果设置为eval e1。然后使用Switch / Case,我想检查v1的模式,如果它不是错误,则递归评估v1e2)并将该值设置为eval e2 。使用这两个值v2v1,然后我对这些值应用另一个函数(v2)。

appVals

我想我可能已经弄明白了,但我并不完全确定我已正确完成开关/外壳部件。有什么想法/建议吗?

4 个答案:

答案 0 :(得分: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 tutorial

接下来让我们制作一个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,我们可以使用<$>,它有点像$,除了它执行您在fmap中定义的任何管道。 同样,<*>有点像函数应用程序,除了额外的管道,所以我们可以定义

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

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

答案 1 :(得分:4)

使用

I left you yesterday(模数重命名)

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"

答案 2 :(得分:3)

我假设你有一些看起来像这样的数据类型:

data Result a = Error String | Success a

问题的解决方案是:

(a)让您的Result输入Monad

(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

答案 3 :(得分:2)

case中,您需要为备选方案设置模式,而在那里不能有/= Error err

此外,=表达式中的->箭头右侧无法case,因此-> eval e1 = v1无效Haskell。

在你的情况下,你只关心它不是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