Parser example doesn't click on me

I am reading through Real World Haskell, and as an introduction to functors / monads, he gives the following example:

parseByte :: Parse Word8
parseByte =
    getState ==> \initState ->
    case L.uncons (string initState) of
      Nothing ->
          bail "no more input"
      Just (byte,remainder) ->
          putState newState ==> \_ ->
          identity byte
        where newState = initState { string = remainder,
                                     offset = newOffset }
              newOffset = offset initState + 1

      

(The rest can be read about a quarter of a way down the page: http://book.realworldhaskell.org/read/code-case-study-parsing-a-binary-data-format.html )

The thing that doesn't make any sense to me is why doesn't this function take any parameters? I expect it to accept a Parse object containing text to be parsed, then return the parsed text and a new Parse object. Instead (as I see it) it "magically" accesses the parser, fetching a byte, and then returns the "modified" parser. Where did the object come from? I've been looking at it all day and still don't know how this feature works.

Any guidance here would be appreciated.

+3


source to share


1 answer


The type is Parse

defined as

newtype Parse a = Parse
    { runParse :: ParseState -> Either String (a, ParseState)
    }

      

So, if you're wondering where the input comes from, it's in the type definition! Each value Parse

completes a function, and then we use ==>

in this example to combine the two Parse

together with composition into a new one Parse

. This is then finally done with runParse

. This function requires ParseState

, defined as

data ParseState = ParseState
    { string :: L.ByteString
    , offset :: Int64
    } deriving (Show)

      

this is what wraps the parsed string.



You can think of a type Parse

as an alias for a type

ParseState -> Either String (a, ParseState)

      

which is the function you are expecting. With a function of ==>

type (with newtype removed)

(==>)
    ::       (ParseState -> Either String (a, ParseState))
    -> (a -> (ParseState -> Either String (b, ParseState)))
    ->       (ParseState -> Either String (b, ParseState))

      

we can take one Parse

and transfer it to another Parse

to create a new one Parse

. All this is nothing more than a fancy wrapper around regular composition of functions. It gets it from where ever it gets runParse

called from the initial state.

+6


source







All Articles