Search code examples
haskellfunctional-programming

How to use input string in a parser


I'm new to Haskell. This is my parser:

data Parser a = MkParser (String -> Maybe a)

This parses any string, gives the first character:

-- anyChar
anyChar :: Parser Char
anyChar = MkParser sf
    where
        sf "" = Nothing
        sf (c:cs) = Just c

It works. Now, I am learning from a tutorial. It says I can convert an answer of a parser and run it through a function, like this (creating a new parser):

-- convert a parsers answer based on a function
convert :: (a -> b) -> Parser a -> Parser b
convert f (MkParser p1) = MkParser sf
    where
        sf inp = case p1 inp of
            Nothing -> Nothing
            Just x -> Just (f x)

This looks like it works! I realized here that I can access the input string in my parser functions. (Even though the definition of convert doesn't take an input string. I want to recreate anyChar so that it also uses the input string (Just so I can understand the syntax and what is going on). (I came from using Python and I'm a rookie at Haskell)

This is what I tried

-- apparently we can use "inp" to signifiy the input string
-- let's recreate anyChar to use input
anyCharInp :: Parser Char
anyCharInp = MkParser sf
    where
        case inp of
            (c:cs) -> Just c
            _ -> Nothing

But it's giving an indentation error. Any ideas?


Solution

  • You are close. Let's focus on this part:

    anyCharInp :: Parser Char
    anyCharInp = MkParser sf
    

    This uses the sf variable, but it does not define it in this line, so we need to define it below using where.

    where
        case inp of
            (c:cs) -> Just c
            _ -> Nothing
    

    Here we do not find a definition for sf. Note that after where the compiler expects definitions, not expressions, and will produce an error if it does not find them. Let's fix that:

    anyCharInp :: Parser Char
    anyCharInp = MkParser sf
        where
        sf inp = case inp of
            (c:cs) -> Just c
            _ -> Nothing
    

    Now sf is defined. Also not how sf is a function taking inp as argument. This is needed, since we need inp to be defined before we can use case inp of ....

    A few alternatives are possible. Here's one that avoids the case of:

    anyCharInp :: Parser Char
    anyCharInp = MkParser sf
        where
        sf (c:cs) = Just c
        sf _      = Nothing
    

    (This turns out to be very similar to the one you posted, though.)

    Here's another, using a lambda expression to avoid where.

    anyCharInp :: Parser Char
    anyCharInp = MkParser (\inp ->
       case inp of
          (c:cs) -> Just c
          _ -> Nothing
       )
    

    More alternatives exist, but these are the most common ones, I believe.