Charlie Harvey

Solved: Real World Haskell Chapter 16 and Parsec3

Another quick fix for an annoyance I found whilst working my way through the Real World Haskell book. This time it’s in Chapter 16 where we are learning about applicative functors and how to use Parsec in an applicative stylee rather than monadically.

As it turns out there have been some changes and the now current Parsec3 already has Applicative and Alternative instances. So the need for a seperate ApplicativeParsec module is obviated, as noted in the chapter comments by Brian Lewis. I made some changes to use the Parsec 3 applicative parts, but then got stuck with the following error generated by the a_hex method No instance for (Text.Parsec.Prim.Stream s0 m0 Char) arising from a use of `char' Possible fix: add an instance declaration for (Text.Parsec.Prim.Stream s0 m0 Char) In the first argument of `(*>)', namely char '%' In the expression: char '%' *> (hexify <$> hexDigit <*> hexDigit) In an equation for `a_hex': a_hex = char '%' *> (hexify <$> hexDigit <*> hexDigit) where hexify a b = toEnum . fst . head . readHex $ [a, ....]

As you can imagine it was all a bit head scratchy, but in the end after some googleing about I found that I had merely to add a type declaration to my a_hex functoin. Presumably there is some kind of problem with the compiler’s ability to infer our type correctly here. I also had to write an a_query function and I put a testFA function in as a convenience. I needed to import Numeric. Finally, I switched round the order of a_hex to make it clearer what was going on as suggested by Petr Prokhorenkov and Paul Keir in the wiki comments. My final code was this.import Text.ParserCombinators.Parsec import Control.Applicative hiding (many,optional,(<|>)) import Numeric a_hex :: CharParser () Char a_hex = char '%' *> (hexify <$> hexDigit <*> hexDigit) where hexify a b = toEnum . fst . head . readHex $ [a,b] a_char :: CharParser () Char a_char = oneOf urlBaseChars <|> (' ' <$ char '+') <|> a_hex a_pair_app1 = liftA2 (,) (many1 a_char) (optionMaybe (char '=' *> many a_char)) a_query :: CharParser () [(String, Maybe String)] a_query = a_pair_app1 `sepBy` char '&' urlBaseChars = ['a'..'z']++['A'..'Z']++['0'..'9']++"$-_.!*'()," testFA = parseTest a_query "foo=bar&a%21=b+c"

That worked as expected and I could move on from parsing URL strings to parsing some JSON!Prelude> :l FormApp.hs [1 of 1] Compiling Main ( FormApp.hs, interpreted ) Ok, modules loaded: Main. *Main> testFA Loading package bytestring-0.9.2.1 ... linking ... done. Loading package transformers-0.3.0.0 ... linking ... done. Loading package mtl-2.1.1 ... linking ... done. Loading package array-0.4.0.0 ... linking ... done. Loading package deepseq-1.3.0.0 ... linking ... done. Loading package text-0.11.2.0 ... linking ... done. Loading package parsec-3.1.2 ... linking ... done. [("foo",Just "bar"),("a!",Just "b c")] *Main> parseTest a_query ("foo=bar&baz=beep&") parse error at (line 1, column 18): unexpected end of input expecting "+" or "%" *Main> parseTest a_query ("foo=bar&baz=beep&moo") [("foo",Just "bar"),("baz",Just "beep"),("moo",Nothing)]


Comments

  • Be respectful. You may want to read the comment guidelines before posting.
  • You can use Markdown syntax to format your comments. You can only use level 5 and 6 headings.
  • You can add class="your language" to code blocks to help highlight.js highlight them correctly.

Privacy note: This form will forward your IP address, user agent and referrer to the Akismet, StopForumSpam and Botscout spam filtering services. I don’t log these details. Those services will. I do log everything you type into the form. Full privacy statement.