首页 > 解决方案 > Haskell Wikibook - 广义代数数据类型练习 - Maybe and Either

问题描述

Haskell Wikibook 的这个页面上有一个练习,它可以让你使用 Maybe 和 Either 来完成一个场景(大概是为了表明它对于用例来说是相当痛苦的)。

练习是:

data Expr = I Int
        | B Bool           -- boolean constants
        | Add Expr Expr
        | Mul Expr Expr
        | Eq  Expr Expr    -- equality test

eval :: Expr -> Maybe (Either Int Bool)
-- Your implementation here.

解决方案的第一行 - 我认为 - 直截了当:

data Expr = I Int         -- integer constants
          | B Bool        -- boolean constants
          | Add Expr Expr -- add two expressions
          | Mul Expr Expr -- multiply two expressions
          | Eq  Expr Expr -- equality test
          deriving (Show)

eval :: Expr -> Maybe (Either Int Bool)
eval (I n) = Just $ Left n
eval (B b) = Just $ Right b
eval (Add e1 e2) = ...
eval (Mul e1 e2) = ...
eval (Eq e1 e2) = ...

但我不确定如何定义其余部分。作为一个例子,我想add我需要解压缩每个表达式的fromLeft, fromJust,但我不确定如何正确地做到这一点(使用模式匹配?)

提前致谢!

标签: haskell

解决方案


是的,使用模式匹配,甚至可能是Maybemonad。

您可以eval (Add e1 e2)仅使用模式匹配来实现分支:

eval (Add e1 e2) = case eval e1 of
    Just (Left i1) -> case eval e2 of
        Just (Left i2) -> Just (Left (i1 + i2))
        _ -> Nothing
    _ -> Nothing

一对上的模式匹配是减少嵌套case语句数量的一种好方法:

eval (Add e1 e2) = case (eval e1, eval e2) of
    (Just (Left i1), Just (Left i2)) -> Just (Left (i1 + i2))
    _ -> Nothing

或者,您可以使用Maybemonad 作为对这些case语句的抽象。Nothing如果块绑定中的任何模式匹配失败do(由于Maybemonad 的实现方式) ,它将自动返回fail

eval (Add e1 e2) = do
    Left i1 <- eval e1
    Left i2 <- eval e2
    return (Left (i1 + i2))

推荐阅读