简体   繁体   中英

How can I rewrite this Haskell using guards with no case-of?

I have three versions of a function laid out below. #2 and #3 work. #2 would be my first choice right now. I'd like to find some way of making #1 work, and see how it compares. It's down at the bottom.

First, I have this type:

data Value =
    IntVal Int
  | BoolVal Bool
  deriving (Show)

And this (working fragment of a) function:

-- #2
evaluate (If ec et ef) s0 =
  case vc of
    (IntVal ____) -> error "Conditional expression in If statement must be a boolean" 
    (BoolVal vcb) -> if vcb then (vt, st)
                            else (vf, sf)
  where (vc, sc) = evaluate ec s0
        (vt, st) = evaluate et sc
        (vf, sf) = evaluate ef sc

I'm wondering if I can get rid of the case statement and use guards instead. I can use guards inside the case statement, I've found this works:

-- #3
evaluate (If ec et ef) s0 =
  case vc of
    (IntVal _) -> error "Conditional expression in If statement must be a boolean" 
    (BoolVal vcb) | vcb       -> (vt, st)
                  | otherwise -> (vf, sf)
  where (vc, sc) = evaluate ec s0
        (vt, st) = evaluate et sc
        (vf, sf) = evaluate ef sc

That's good to know. I think #2 most clearly communicates my intentions. Nonetheless, here's the first thing I tried, and I'd like to figure out how it could actually work:

-- #1
evaluate (If ec et ef) s0 
  | (IntVal vc) = error "Conditional expression in If statement must be a boolean" 
  | vc == True  = (vt, st)
  | otherwise   = (vf, sf)
  where (vc, sc) = evaluate ec s0
        (vt, st) = evaluate et sc
        (vf, sf) = evaluate ef sc

Which gives the following errors. It seems like I need something that will ask if something of the Value type is also of the IntVal subtype?:

Couldn't match expected type `Int' with actual type `Value'
In the first argument of `IntVal', namely `vc'
In the expression: (IntVal vc)

--------------------------------------------------------------------------------

Couldn't match expected type `Bool' with actual type `Value'
In the expression: (IntVal vc)
In a stmt of a pattern guard for
               an equation for `evaluate':
  (IntVal vc)

How can I fix these errors and have a no-cases, guards-only implementation?

This isn't really an answer, but I'd write the whole thing something like this:

evaluate (If ec et ef) s0 =
    case evaluate ec s0 of
    (True,  sc) -> evaluate et sc
    (False, sc) -> evaluate ef sc

I find the naming of vt , st , vf , and sf to be harder to read because I have to trace the data flow of those variables myself. Keeping them unnamed simplifies understanding.

The problem with your #1 is that ordinary guards can only be Boolean expressions, which have no power to succinctly match patterns. So IntVal vc just won't work as one. And even if it did, the vc you get from an IntVal vc pattern cannot be used as a Bool value - you need the BoolVal vc pattern for that.

However, you can use pattern guards :

evaluate (If ec et ef) s0
  | IntVal _      <- vc = error "Conditional expression in If statement must be a boolean" 
  | BoolVal True  <- vc = (vt, st)
  | BoolVal False <- vc = (vf, sf)
  where (vc, sc) = evaluate ec s0
        (vt, st) = evaluate et sc
        (vf, sf) = evaluate ef sc

If you redefine the type Value using record syntax :

data Value = IntVal { unInt :: Int } | BoolVal { unBool :: Bool }

Then you can do:

evaluate (If ec et ef) s0 =
  if unBool vc then (vt, st) else (vf, sf)
  where (vc, sc) = evaluate ec s0
        (vt, st) = evaluate et sc
        (vf, sf) = evaluate ef sc

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM