Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The type signature of Parsec function 'parse' and the class 'Stream'

What does the constraint (Stream s Identity t) mean in the following type declaration?

parse :: (Stream s Identity t)
  => Parsec s () a -> SourceName -> s -> Either ParseError a

What is Stream in the following class declaration, what does it mean. I'm totally lost.

class Monad m => Stream s m t | s -> t where

When I use Parsec, I get into a jam with the type-signatures (xxx :: yyy) all the time. I always skip the signatures, load the src into ghci, and then copy the type-signature back to my .hs file. It works, but I still don't understand what all these signatures are.


EDIT: more about the point of my question.

I'm still confused about the 'context' of type-signature:

(Show a) =>

means a must be a instance of class Show.

(Stream s Identity t) => 

what's the meaning of this 'context', since t never showed after the =>


I have a lot of different parser to run, so I write a warp function to run any of those parser with real files. but here comes the problem:

Here is my code, It cannot be loaded, how can I make it work?

module RunParse where
import System.IO
import Data.Functor.Identity (Identity)
import Text.Parsec.Prim (Parsec, parse, Stream)

--what should I write "runIOParse :: ..."
--runIOParse :: (Stream s Identity t, Show a) => Parsec s () a -> String -> IO ()
runIOParse pa filename =
  do
    inh <- openFile filename ReadMode
    outh <- openFile (filename ++ ".parseout") WriteMode
    instr <- hGetContents inh
    let result = show $ parse pa filename instr
    hPutStr outh result
    hClose inh
    hClose outh
like image 517
wuxb Avatar asked Jun 16 '11 09:06

wuxb


2 Answers

the constraint: (Stream s Identity t) means what?

It means that the input s your parser works on (i.e. [Char]) must be an instance of the Stream class. In the documentation you see that [Char] is indeed an instance of Stream, since any list is.

The parameter t is the token type, which is normally Char and is determinded by s, as states the functional dependency s -> t.

But don't worry too much about this Stream typeclass. It's used only to have a unified interface for any Stream-like type, e.g. lists or ByteStrings.

what is Stream

A Stream is simply a typeclass. It has the uncons function, which returns the head of the input and the tail in a tuple wrapped in Maybe. Normally you won't need this function. As far as I can see, it's only needed in the most basic parsers like tokenPrimEx.

Edit:

what's the meaning of this 'context', since t never showed after the =>

Have a look at functional dependencies. The t never shows after the ´=>´, because it is determiend by s. And it means that you can use uncons on whatever s is.

Here is my code, It cannot be loaded, how can I make it work?

Simple: Add an import statement for Text.Parsec.String, which defines the missing instance for Stream [tok] m tok. The documentation could be a bit clearer here, because it looks as if this instance was defined in Text.Parsec.Prim.

Alternatively import the whole Parsec library (import Text.Parsec) - this is how I always do it.

like image 103
bzn Avatar answered Nov 09 '22 23:11

bzn


The Stream type class is an abstraction for list-like data structures. Early versions of Parsec only worked for parsing lists of tokens (for example, String is a synonym for [Char], so Char is the token type), which can be a very inefficient representation. These days, most substantial input in Haskell is handled as Text or ByteString types, which are not lists, but can act a lot like them.

So, for example, you mention

parse :: (Stream s Identity t)
  => Parsec s () a -> SourceName -> s -> Either ParseError a

Some specializations of this type would be

parse1 :: Parsec String () a -> SourceName -> String -> Either ParseError a
parse2 :: Parsec Text () a -> SourceName -> Text -> Either ParseError a
parse3 :: Parsec ByteString () a -> SourceName -> ByteString -> Either ParseError a

or even, if you have a separate lexer with a token type MyToken:

parse4 :: Parsec [MyToken] () a -> SourceName -> [MyToken] -> Either ParseError a

Of these, only the first and last use actual lists for the input, but the middle two use other Stream instances that act enough like lists for Parsec to work with them.

You can even declare your own Stream instance, so if your input is in some other type that acts sort of list-like, you can write an instance, implement the uncons function, and Parsec will work with your type, as well.

like image 37
Chris Smith Avatar answered Nov 09 '22 22:11

Chris Smith