0

I'm working on an instance of Read ComplexInt.

Here's what was given:

data ComplexInt = ComplexInt Int Int
deriving (Show)

and

module Parser (Parser,parser,runParser,satisfy,char,string,many,many1,(+++)) where

import Data.Char
import Control.Monad
import Control.Monad.State

type Parser = StateT String []

runParser :: Parser a -> String -> [(a,String)]
runParser = runStateT

parser :: (String -> [(a,String)]) -> Parser a
parser = StateT

satisfy :: (Char -> Bool) -> Parser Char
satisfy f = parser $ \s -> case s of
    [] -> []
    a:as -> [(a,as) | f a]

char :: Char -> Parser Char
char = satisfy . (==)

alpha,digit :: Parser Char
alpha = satisfy isAlpha
digit = satisfy isDigit

string :: String -> Parser String
string = mapM char

infixr 5 +++
(+++) :: Parser a -> Parser a -> Parser a
(+++) = mplus

many, many1 :: Parser a -> Parser [a]
many p = return [] +++ many1 p
many1 p = liftM2 (:) p (many p)

Here's the given exercise:

"Use Parser to implement Read ComplexInt, where you can accept either the simple integer 
syntax "12" for ComplexInt 12 0 or "(1,2)" for ComplexInt 1 2, and illustrate that read 
works as expected (when its return type is specialized appropriately) on these examples. 
Don't worry (yet) about the possibility of minus signs in the specification of natural 
numbers."

Here's my attempt:

data ComplexInt = ComplexInt Int Int
    deriving (Show)

instance Read ComplexInt where
    readsPrec _ = runParser parseComplexInt

parseComplexInt :: Parser ComplexInt
parseComplexInt = do
    statestring <- getContents
    case statestring of
        if '(' `elem` statestring 
            then do process1 statestring
            else do process2 statestring
    where
    process1 ststr = do
        number <- read(dropWhile (not(isDigit)) ststr) :: Int
        return ComplexInt number 0
    process2 ststr = do
        numbers <- dropWhile (not(isDigit)) ststr
        number1 <- read(takeWhile (not(isSpace)) numbers) :: Int
        number2 <- read(dropWhile (not(isSpace)) numbers) :: Int
        return ComplexInt number1 number2

Here's my error (my current error, as I'm sure there will be more once I sort this one out, but I'll take this one step at time):

Parse error in pattern: if ')' `elem` statestring then
                            do { process1 statestring }
                        else
                            do { process2 statestring }

I based my structure of the if-then-else statement on the structure used in this question: "parse error on input" in Haskell if-then-else conditional

I would appreciate any help with the if-then-else block as well as with the code in general, if you see any obvious errors.

1
  • Here are a couple more errors you'll be dealing with soon: not(isDigit) and not(isSpace) should be not . isDigit and not . isSpace; and the do and <- notations are only for monads, but you seem like you just want let ... in. Commented Nov 9, 2013 at 22:05

2 Answers 2

7

Let's look at the code around the parse error.

case statestring of
    if '(' `elem` statestring 
        then do process1 statestring
        else do process2 statestring

That's not how case works. It's supposed to be used like so:

case statestring of
    "foo"  ->  -- code for when statestring == "foo"
    'b':xs ->  -- code for when statestring begins with 'b'
    _      ->  -- code for none of the above

Since you're not making any sort of actual use of the case, just get rid of the case line entirely.

(Also, since they're only followed by a single statement each, the dos after then and else are superfluous.)

Sign up to request clarification or add additional context in comments.

Comments

1

You stated you were given some functions to work with, but then didn't use them! Perhaps I misunderstood. Your code seems jumbled and doesn't seem to achieve what you would like it to. You have a call to getContents, which has type IO String but that function is supposed to be in the parser monad, not the io monad.

If you actually would like to use them, here is how:

readAsTuple :: Parser ComplexInt
readAsTuple = do
  _ <- char '('          
  x <- many digit
  _ <- char ','
  y <- many digit
  _ <- char ')'
  return $ ComplexInt (read x) (read y)

readAsNum :: Parser ComplexInt
readAsNum = do
  x <- many digit
  return $ ComplexInt (read x) 0

instance Read ComplexInt where
  readsPrec _ = runParser (readAsTuple +++ readAsNum)

This is fairly basic, as strings like " 42" (ones with spaces) will fail.

Usage:

> read "12" :: ComplexInt 
ComplexInt 12 0
> read "(12,1)" :: ComplexInt
ComplexInt 12 1

The Read type-class has a method called readsPrec; defining this method is sufficient to fully define the read instance for the type, and gives you the function read automatically.

What is readsPrec?

readsPrec :: Int -> String -> [(a, String)]. The first parameter is the precedence context; you can think of this as the precedence of the last thing that was parsed. This can range from 0 to 11. The default is 0. For simple parses like this you don't even use it. For more complex (ie recursive) datatypes, changing the precedence context may change the parse.

The second parameter is the input string.

The output type is the possible parses and string remaining a parse terminates. For example:

>runStateT (char 'h') "hello world"
[('h',"ello world")]

Note that parsing is not-deterministic; every matching parse is returned.

>runStateT (many1 (char 'a')) "aa"
[("a","a"),("aa","")]

A parse is considered successful if the return list is a singleton list whose second value is the empty string; namely: [(x, "")] for some x. Empty lists, or lists where any of the remaining strings are not the empty string, give the error no parse and lists with more than one value give the error ambiguous parse.

2 Comments

Thank you! The code now compiles fine following your directions. However, I'm also confused as to how to use this code. How do I test that the input "12" will return ComplexInt 12 0, and that the input (12,10) will return ComplexInt 12 10?
@user2967411 Edited the answer with a response to your question, as well as some extra information.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.