2
votes

I am reading the book Programming in Haskell 2nd by Graham Hutton. https://www.cs.nott.ac.uk/~pszgmh/pih.html#slides

When it comes to chapter 13.4 Sequencing parsers, it contains the following functions:

> parse three "abcdef" 
[((’a’,’c’),"def")] 
> parse three "ab" 
[]

I would like to understand what are the intermediate steps to evaluate them behind the scene. You can find the working source code for the Functor and Applicative for the Parser here:

import Control.Applicative
import Data.Char

-- Basic definitions
newtype Parser a =
  P (String -> [(a, String)])
parse (P p) inp = p inp


item :: Parser Char
item = P (\inp -> case inp of
         [] -> []
         (x:xs) -> [(x, xs)])

-- Sequencing parsers
instance Functor Parser
   -- fmap :: (a -> b) -> Parser a -> Parser b
                                               where
  fmap g p =
    P
      (\inp ->
         case parse p inp of
           [] -> []
           [(v, out)] -> [(g v, out)])
--parse  (fmap toUpper item) "abc"

instance Applicative Parser
   -- pure :: a -> Parser a
                            where
  pure v = P (\inp -> [(v, inp)])
   -- <*> :: Parser (a -> b) -> Parser a -> Parser b
  pg <*> px =
    P
      (\inp ->
         case parse pg inp of
           [] -> []
           [(g, out)] -> parse (fmap g px) out)

three :: Parser (Char, Char)
three = pure g <*> item <*> item <*> item
  where
    g x y z = (x, z)

I have attempted it by substituting the definitions from Functor and Applicative in the following way, but dont know how to do it further:


parse three "abc"



three :: Parser (Char,Char) 
three 
= pure g <*> item <*> item <*> item 
=P (\inp -> [(g,inp)]) <*> item <*> item <*> item        (apply pure v)

-----------------------------------------------------------------------------------------------------------
=P (
\inp -> case parse P (\inp -> [(g,inp)]) inp of         (apply pg <*> px)
[] -> [] 
[(g,out)] -> parse (fmap g item) out
)

<*> item <*> item 
-----------------------------------------------------------------------------------------------------------
=P (
\inp -> case (\inp -> [(g,inp)]) inp of             (apply parse (P p) inp = p inp
[] -> [] 
[(g,out)] -> parse (fmap g item) out
)

<*> item <*> item 

-----------------------------------------------------------------------------------------------------------
Here, inp=”abc”,  (\inp -> [(g,inp)]),  inp = [  (f x y z =(x,z), “abc”   )]  
=P (
parse (
fmap g item
) out
)                               (apply \inp -> [(g,inp)] on inp)

<*> item <*> item 

2

2 Answers

1
votes

(This is in response to the 'Based on the suggestion of @Daniel Wagner, I expand on fmap g p:' update)

Is the last substitution correct?

It's impossible to answer because steps before that are incorrect.

There are several problems with your expansion, indicating that you're being sloppy when writing, which has led to mistakes. There might also be conceptual problems.

For example, when you inline three = P ... into parse three "abc", you didn't put parentheses around P ..., leading to this line:

parse P (parse (P ...)) <*> item <*> item "abc"

This is most likely syntactically incorrect, as it would be parsed like

(parse P (parse (P ...))) <*> item <*> (item "abc")

While you likely meant:

parse ((P ...) <*> item <*> item) "abc"

If you think, well I'm just doing this to make things easier to write, then check this out: This syntax error also led you to erroneously work on the parse P (parse (P ...)) part independently of <*> item <*> item "abc", which a serious mistake and made most of everything following that irrelevant.

Another thing is this:

Here, inp="abc",  (\inp -> [(g,inp)]),  inp = [  (f x y z =(x,z), "abc"   )]

This line makes no sense at all. Since you are just expanding three, It's not valid to say that inp is anything. Consider (\x -> x). The x here is merely to establish the relationship that the result is the same as the argument, and is not any particular value. This is what is meant by it being a bound variable.

(And I don't even know what you're talking about when you say (\inp -> [(g,inp)]), inp = [ (f x y z =(x,z), "abc" )]. Maybe you can clarify?)

This also means that the following makes no sense

 (\inp -> case parse item inp of [] -> []; [(v, out)] -> [(g v, out)]))<*> item <*> item “abc”
={substitute inp for "abc"}
 case parse item  "abc" of [] -> []; [(v, out)] -> [(g v, out)]<*> item <*> item 

There are multiple problems here. To begin with, the first line has an extra close paren, which makes it hard to see what you mean. If we ignore that, then before you had (\inp ->) <*> item ..., but afterwards you did not put parens around the case expression, making <*>.

Also, it seems that you want to do a beta-reduction here. A beta reduction always has the form (\v -> E) a, in which the lambda is directly applied to an argument. You cannot just say randomly that 'v is equal to a' and jump around in expressions.

For example, If we have f (\x -> x + 1) 3, is it right to reduce that to f 4? No, because the lambda isn't being applied to 3.

This means that even if the first half is right, the second half of what you wrote is based on a nonsensical step and is irrelevant.


I would very much want to tell you how to fix your reduction, but I'm sorry to say that I think what you wrote is beyond repair by now. If you want to have a correct reduction trace, please be more careful with both the syntax and the validity of each step, and do everything again from scratch.

As an aid, there are several things you should check to see if things went wrong:

  1. Each step should be syntactically valid. No unbound variables, no missing parens, etc.
  2. If the original expression typechecks, then each step must also typecheck and have the same type.
1
votes

It's a good start. Your last step doesn't look quite right. Stripping off the shared parts, you've written that

\inp -> case (\inp -> [(g,inp)]) inp of [] -> []; [(g, out)] -> parse (fmap g item) out
=
                                                                parse (fmap g item) out

This equation doesn't look quite right to me: the former is a function, and the latter isn't. Additionally, the latter refers to the free variable out, while the former doesn't (because out is bound by the pattern match it's enclosed in). A more careful continuation looks like this:

\inp -> case (\inp -> [(g,inp)]) inp of [] -> []; [(g, out)] -> parse (fmap g item) out
= { beta reduction, substituting inp for inp }
\inp -> case [(g, inp)]              of [] -> []; [(g, out)] -> parse (fmap g item) out
= { case reduction, substituting g for g and inp for out }
\inp ->                                                         parse (fmap g item) inp

If you like, you could then eta-reduce this to parse (fmap g item). Plugging this back into the shared parts we dropped above, we have:

three
=
P (parse (fmap g item)) <*> item <*> item

The result can be verified in ghci:

*Parsing> parse three "abc"
[(('a','c'),"")]

*Parsing> let g x y z = (x,z)

*Parsing> parse (P (parse (fmap g item)) <*> item <*> item) "abc"
[(('a','c'),"")]

As next steps, there are three places you could do your next definition-expansion to enable further progress:

  1. You could expand the fmap in fmap g item.
  2. You could expand the inner (<*>) in P (...) <*> item.
  3. You could expand the outer (<*>) in (P (...) <*> item) <*> item.

Good luck!