mudri
mudri

Reputation: 770

Parse error in pattern x * y (of case statement)

I have this code:

module BalancedTwoDozenMultDrill where
import BalancedTwoDozenLib
myRandoms :: Int -> IO [Int]
myRandoms n = let x = 24^n `div` 2 in randomRs (-x,x) <$> getStdGen
drill :: [Int] -> IO ()
drill (x:y:rs) = do
    putStr $ showInt x ++ " × " ++ showInt y ++ " = "
    a <- getLine
    case a of
        "" -> return ()
        showInt (x * y) -> do   -- <= here
            putStrLn "Correct"
            drill rs
        _ -> do
            putStrLn $ "Wrong; " ++ showInt (x * y)
            drill rs
main :: IO [Int]
main = drill =<< myRandoms =<< readLn

and get error:

BalancedTwoDozenMultDrill.hs:11:18: Parse error in pattern: x * y

However, replacing part of the case statement with:

    -- ...stuff
    let i = showInt (x * y)
    case a of
        "" -> return ()
        i -> do
        -- stuff...

Makes it parse (it goes to “not in scope” errors, which I can fix). The only reason I see for the first fragment being wrong is that there is function application going on. Is it true that I can't use ordinary function application for the alternatives in a case statement?

Upvotes: 1

Views: 1030

Answers (2)

David Unric
David Unric

Reputation: 7719

Although an answer already accepted I'd just mention there is a bit tricky way how use a boolean expressions in a case expression - by using guards:

   case () of
       _
        | a == "" -> return ()
        | showInt (x * y) -> do   -- <= here
            putStrLn "Correct"
            drill rs
        | otherwise -> do
            putStrLn $ "Wrong; " ++ showInt (x * y)
            drill rs

Upvotes: 4

bheklilr
bheklilr

Reputation: 54068

When you have a pattern in a case statement, it has to follow the same rules as that in pattern matching on function arguments. Only literals, constructors, and the wildcard _ can be matched on, not function applications. Instead, you could do something more like

a <- getLine
let xyStr = showInt (x * y)        -- Avoid recomputation with a let binding
when (not $ null a) $ do
    if a == xyStr
        then do
            putStrLn "Correct"
            drill rs
        else do
            putStrLn $ "Wrong; " ++ xyStr
            drill rs

You'll need to import when from Control.Monad, though.


The reason why you have to follow the same rules in case statements as in pattern matching in function definitions is because the compiler actually converts something like

head :: [a] -> a
head (x:xs) = x
head _ = error "Prelude.head: empty list"

Into

head :: [a] -> a
head list = case list of
    (x:xs) -> x
    _      -> error "Prelude.head: empty list"

The only reason we have the former version is convenience, it often makes for prettier looking code.

This link should be able to give you a more thorough explanation as to what is and isn't valid pattern matching constructs.


The other problem you had was trying to replace showInt (x * y) with i where let i = showInt (x * y). When you do this, you first bind the value showInt (x * y) to the name i, then in your case statement you have the patterns

"" -> ...
i  -> ...
_  -> ...

So now your pattern is i, and it will act like a catch-all pattern after "". This rebinds the name i for the scope of that case statement.

A good rule to keep in mind is that you can't pattern match against a value obtained at run time, you have to check with equality or other comparison operations.

Upvotes: 6

Related Questions