10

can someone help me to understand how to use Applicative style for writing Parsec parsers? This is the code i have:

module Main where
import Control.Applicative hiding (many)
import Text.Parsec
import Data.Functor.Identity
data Cmd = A | B deriving (Show)

main = do
  line <- getContents
  putStrLn . show $ parseCmd line

parseCmd :: String -> Either ParseError String
parseCmd input =  parse cmdParse "(parser)" input

cmdParse :: Parsec String () String
cmdParse = do
  slash <- char '/'
  whatever <- many alphaNum
  return (slash:whatever)

cmdParse2 :: String -> Parsec String () String
cmdParse2 = (:) <$> (char '/') <*> many alphaNum

but when i try to compile it, i get following:

/home/tomasherman/Desktop/funinthesun.hs:21:13:
    Couldn't match expected type `Parsec String () String'
                with actual type `[a0]'
    Expected type: a0 -> [a0] -> Parsec String () String
      Actual type: a0 -> [a0] -> [a0]
    In the first argument of `(<$>)', namely `(:)'
    In the first argument of `(<*>)', namely `(:) <$> (char '/')'
Failed, modules loaded: none.

The idea is that i want cmdParse2 to do same thing that cmdParse does, but using applicative stuff...my approach is probably completely wrong, i'm new to haskell

Arg
  • 1,926
  • 21
  • 32
  • Your error is as if you had written `(++) <$> ...` not `(:) <$> ...`. – huon Oct 25 '12 at 09:15
  • sorry, fixed .. i was trying it with both ++ and : and got it mixed up – Arg Oct 25 '12 at 09:19
  • 4
    The ultimate in well-researched questions, because the answer is "Well done, sound choice, done correctly, but fix this minor type error." +1 – AndrewC Oct 25 '12 at 15:12

2 Answers2

5

Your applicative usage is spot on, you just have an incorrect signature. Try:

cmdParse2 :: Parsec String () String
shang
  • 24,642
  • 3
  • 58
  • 86
  • just a quick question, is it possible to get type Text from 'many alphaNum'? so i don't always pack it manually if i want to use Text instead of String? – Arg Oct 25 '12 at 10:16
  • @Arg 1. Yes. You could convert: `(pack <$> many alphaNum)`, but you'll need to convert everything: `cmdParse3 = cons <$> char '/' <*> (pack <$> many alphaNum)` but that's better written `cmdParse4 = pack <$> cmdParse2`, and it might be better, depending on your application to leave it till the end and do `pack <$> myTopLevelParser`. (You might be interested in `Text.Parsec.Text` to change the source data too.) 2. Why? You're building up strings a character at a time, combining them the good way. `append` is _O(n)_ for Text anyway. – AndrewC Oct 25 '12 at 15:09
  • ok, thanks for the tip. Btw, so awesome that haskell has function complexities for it's operations in documentation – Arg Oct 26 '12 at 06:48
4

Your approach looks correct to me, the problem is that cmdParse2 has the wrong type. It should have the same type as cmdParse. By the way, you can omit the parens around char '/' in the applicative style parser.

Jan Christiansen
  • 3,153
  • 1
  • 19
  • 16