 My name is James Brock and I work at the AI Consultancy Company Cross Compass in Tokyo. This is my talk, Monadic Parsers at the Input Boundary for PureCons 2022. I'm a maintainer for the PureScript Parsing Library and the examples in this talk will use syntax and type names from that library. This talk is for an audience who has some familiarity with regular expressions and monads. A process running on a computer is isolated from other processes on that computer and from the rest of the world. On that isolation boundary, the process has input and output. The process views its inputs and outputs as either discrete events like signals and mouse clicks or as byte streams. The term byte stream includes any string like thing like file names, web browser, form field values, entire files, or anything else represented in process memory as an array of bytes. We want to talk about byte streams. For output, a process serializes byte streams. Serializing a byte stream is easy. We write a byte stream and we send it. There are no surprises. For input, a process deserializes a byte stream. Deserializing a byte stream is hard. When a process reads a byte stream, it must somehow turn the information in the byte stream into a data structure in the process's native language. Reading a byte stream is difficult because there may be surprises. We expect the byte stream to have a certain structure, but it may not have that structure. A large portion of process bugs, crashes, and security vulnerabilities can be characterized as a process misbehaving when it encounters surprises in an input byte stream. We have many different terms we use to talk about deserializing a byte stream, including decoding, validating, lexing, tokenizing, and pattern matching. These different terms describe activities which are all essentially similar. We will say that all of this means parsing. It is the act of reading and parsing input byte streams that we will focus on in this talk. We will be talking mostly about unicode strings, but everything in this talk will generalize to any kind of byte stream. In the year 2022, here are some common methods for parsing an input byte stream. Write an ad hoc parser based on string splitting and regular expressions. This is how we end up writing a process which misbehaves on surprises. It's easy to make mistakes here and forget to handle certain cases. Use a parser generator like Google protocol buffers. This works great as long as the input is in this format, but that is often not true. Use JSON. Again, this obviously only works if the input is JSON. The reason why such a huge amount of network traffic these days is JSON is exactly because reading a byte stream is difficult. And good days JSON parsers already exist for every language. People would rather mangle their data into JSON than write a custom byte stream parser. And lastly, monadic parser combinators. I'll try to convince you that monadic parsing is the best method for parsing any arbitrary input byte stream. And this is always the first method you should try when you are reading a byte stream from over the process input boundary. I'll read to you from the essay parse don't validate by Alexis King. Consider what is a parser? Really a parser is just a function that consumes less structured input and produces more structured output. By its very nature, a parser is a partial function. Some values in the input do not correspond to any value in the output. So all parsers must have some notion of failure. Under this flexible definition parsers are an incredibly powerful tool. They allow discharging checks on input upfront right on the boundary between a program and the outside world. And once those checks have been performed, they never need to be checked again. Haskellers are well aware of this power. A parser sits on the boundary between your application and the external world. That world doesn't speak in product and some types but in streams of bytes so there's no getting around the need to do some parsing. Doing that parsing upfront before acting on the data can go a long way toward avoiding many classes of bugs, some of which might even be security vulnerabilities. So Alexis King is discussing the abstract idea of what it means to parse something. She does not talk about monadic parsers or any kind of monads at all. Her emphasis is on taking an unstructured input like a byte stream and turning it into a data structure. That data structure should ideally make illegal states unrepresentable. This is a common proverb in the functional programming world. It's only possible to do this in programming languages which have a strong enough type system that the compiler can check your code for you and prove that certain things will always be true and certain variants will hold. If we parse our input byte stream into a data structure which makes illegal states unrepresentable, then by producing an instance of the data structure we have provided a proof that our input byte stream is in some sense legal. The easiest way to do this is with monadic parsers. A parsing monad is a monad with three features. It knows its position in the input string. It can choose alternate parsing branches based on the contents of the input string, and it can fail in the case that the input string is illegal and cannot be parsed. There are many implementations of monadic parser combinators in many languages and all of them have these features. These three features are necessary for a parsing monad and they are also sufficient. Any monad which has these three features is a parsing monad. Let's look at an example of matching a pattern with a monadic parser. Here is the same pattern expressed with a regular expression and with a monadic parser. The pattern which we want to match is a string that starts with a lowercase a character and then has either a lowercase b character or an uppercase b character. Then we also want to capture the b character, whether it was lowercase or uppercase. In the regular expression, we capture the b by surrounding it with parentheses. In the monadic parser, we capture the b character by returning it from the monadic computation. Let's look at the monadic parser computation. It's in the form of a do block. On the first line of the do block, we match a literal a character and then throw it away by binding it to the underscore variable. We don't need to bind it to a named variable because after we've matched it, then we don't need it anymore. Now remember that the parser monad has state inside of it, which tracks the current position in the input string. The act of successfully matching the a character will step the current input string position forward by one character. On the next line of the do block, we match a lowercase or uppercase b character and bind it to a variable named x. The way that we say that x can be either a lowercase b or uppercase b is by using the alternative operator. The alternative operator is kind of like the or operator, which is why we write it as a bracketed vertical pipe character. The alternative operator is a binary operator, which takes two parsers as arguments. It first tries the left parser and if that succeeds, then it returns the result. If the left parser fails, then it tries the right parser and the returns the result of that. So then after this alternative parser succeeds, the x variable will contain either lowercase b or uppercase b. Then we return the variable x from the monad computation. Now let's modify this parser slightly so that instead of returning the captured b character returns a data structure. Let's return a very simple data structure. What is the simplest data structure. It's a single bit. And the single bit contains all the information we need about whether the b character was uppercase or lowercase. So now we've changed the type of parser to return a Boolean instead of a care. The parser returns true if the parsed succeeded and the b character was uppercase. This is an example of what we mean when we say that we want to return typed data structures which make illegal states unrepresentable. There are only two possible ways to successfully parse this string and the data structure which we're returning has exactly two possible values and no more. Recall that I said that a monadic parser needs three features, the state of the current position in the input string, alternative and failure. Let's see what happens when the AB parser fails. We give it an illegal string AXXX and instead of returning right and a Boolean data structure, it returned left with a description at a position for the error. The error says that it failed to parse because it was expecting a b character at position two. When we're parsing patterns out of a string, a common thing to want to do is pattern repetition. Let's try repeating the AB pattern. We added the asterisk quantifier to the regular expression to repeat the pattern many times. We also defined a new parser named AB many. The AB parser uses the many parser combinator to match the AB pattern many times. Let's talk about what we mean by a parser combinator. A parser combinator is a normal pure script function. It is a function which takes a parser as an argument and then returns a new parser. The type of the data structure produced by the new parser may be different. In this example, you can see that the AB parser has type parser string Boolean because it is a parser from a string to a Boolean. We passed the AB parser as an argument to the many parser combinator and the many parser combinator returned a new parser with type parser string array Boolean. The data structure produced by the new parser will be an array of Booleans. So we run this new AB many parser and it matches the AB pattern as many times as it can on the input string and then returns an array, which is true for each of the matched AB patterns which had an uppercase B character. Parser combinators may take more than one parser argument. For example, the alternative operator which we are using in the AB parser to match either a lowercase B character or an uppercase B character is also in fact a parser combinator. That alternative binary operator is a function which takes two arguments, a left parser and a right parser. It returns a new parser which tries first the left parser and then tries the right parser. So parsers call parser combinators and pass parsers as arguments to the parser combinators which return new parsers. This is how we build up parsers for complicated pattern matching. Let's try writing our own parser combinator. Here is a parser combinator, which we have named twice. It is a lot like the many combinator and in fact it has the same type signature. The difference between the many combinator and the twice combinator is that the many combinator will try to match its argument parser as many times as possible, but the twice combinator will match his argument parser exactly two times, no more no less. The twice combinator takes one parser as an argument, which we have named P. The P parser can be any type of parser, which is what we mean by for all a the type parameter for the P parser is named a. The twice combinator will first try to match the P parser and bind the result to the name P one, then it will try to match the P parser again and bind the result to the name P two. If both of those succeed, then it will return an array with P one and P two. Down below we define and run a parser named AB twice with the same input as before. You can see that the AB twice parser matches the AB pattern two times and returns true when the matched pattern has an uppercase B character. So we can see that regular expressions and monadic parsers both solve essentially the same pattern matching problem, but the monadic parser is much longer than the regular expression and has a lot more code. So to add the thing about regular expressions is that they seem like a reasonable and efficient solution for small toy problem examples like these. But when we take on larger and more complex problems the advantage of monadic parsers becomes apparent. That's analogous to the situation of pure functional programming. It's difficult to convey the advantage of pure functional programming with small toy examples because small toy programs are simple in any language. The advantages of pure functional programming become truly apparent when we are maintaining and refactoring and improving large complicated computer programs. In the same way the advantage of monadic parsing over regular expressions becomes apparent when we are parsing more complicated patterns. Let's parse something a little bit harder. How about an email address. We all have a pretty good idea what the format for an email address is. So here is the complete internet engineering task force specification for the format of an email address. Okay, that's a little bit hard. It's not quite as simple as we might have expected but it's not too bad. Let's think about what it says the forward slashes in the syntax mean alternative, which means either the one on the left or the one on the right. The square bracket syntax means that something is optional. So it's okay if that thing is missing. First the specification says that an address is either a mailbox or a group, then it says that a mailbox is either a name adder or an adder spec, then it says that a name adder is an optional display name followed by an angle adder. Then it says that an angle adder is an optional comment folding white space followed by a left angle bracket character followed by an adder spec followed by a right angle bracket character followed by an optional comment folding white space. Or, alternately, it can be an ob's angle adder. And it goes on like this. Can we write a monadic parser to parse an email address? Here's a monadic parser for parsing IETF email addresses written by Fraser Tweedel and published in the Haskell library purebred email. This is in the Haskell language and uses the Addo Parsec parsing library. The syntax for PureScript using the PureScript parsing library would be almost exactly the same. The monadic parser looks a lot like the internet engineering task force specification. Let's compare the specification and the monadic parser line by line. First, the spec says that an address is either a mailbox or a group. The monadic parser says that an address is either a group or a single mailbox. So the order of the alternative was flipped, but that's fine. I'm sure Fraser Tweedel had good reasons for doing that. Next, the spec says that a mailbox is either a name adder or an adder spec. In the monadic parser, I see address spec on the right side of the alternative. And that expression on the left side must be equivalent to a name adder, I guess. There is an optional display name. The optional function is a parser combinator, which does exactly what you'd expect. It will try to match the display name pattern one time. But if the display name pattern is not there, then it skips it. Next, the spec says that an angle adder is this adder spec expression surrounded by angle bracket characters and optional comment folding white space expressions or, alternately, an obs angle adder. The monadic parser says basically the same thing. I don't see the obs angle adder alternative in the monadic parser for angle adder. Maybe we should open an issue with Fraser Tweedel and ask him about this. Finally, the spec says that a mailbox list is either a comma-separated list of mailboxes or, alternately, an obs inbox list. And the monadic parser also says that a mailbox list is a comma-separated list of mailboxes. Again, the monadic parser omits the alternative. I don't know why. The point is that the formal spec for RSC 5322 and the monadic parser implemented in Haskell are very similar. We can see what the monadic parser is doing. And we can ask ourselves reasonable questions about the implementation. Next, let's look at the same RFC 5322 spec implemented as a regular expression. The author of this regular expression claims that it, quote, 99.99% works for parsing RFC 5322 email addresses. And he may be right about that, but how can we tell? The regular expression for RFC 5322 is shorter than the monadic parser, but it's very difficult to read it or make improvements. This is why regular expressions are included in the pejorative Wikipedia article about write-only programming languages. Here's a quote from the article. Write-only code is source code so arcane, complex, or ill-structured that it cannot be reliably modified or even comprehended by anyone with the possible exception of the author. Now, email addresses didn't start out as an internet engineering task force specification. They were intended to be simple and they started out simple. In the 1970s, an email address was a username and then an at sign and then the name of a computer. We could parse that with a regular expression, but over time, email addresses became more complicated as everything does. Monadic parsers will scale and grow in a much more maintainable way than regular expressions and even small patterns written as a monadic parser will be easier for others to read. We often use regular expressions to scan a string and capture all the patterns which we find. Here we want to find all of the integers in the string 10x2y-3 and split them out. We've done this with both a regular expression and a monadic parser. There are a couple of things to notice here. First, the monadic parser for an integer doesn't just match a string pattern and return a string, it actually converts the string to an integer and returns the integer. We're using the int decimal parser which is included in the pure script parsing library. The split cap function runs the int decimal parser on this string and produces for us a fully typed data structure, which tells us everything there is to know about the structure of the input string with respect to the integer patterns in the string. The second thing to notice is that we made a mistake when we are writing the integer pattern for the regular expression. We forgot to think about whether we want to allow negative integers. Now it's easy to change our regular expression so that it will allow negative integers and I'm sure you all know how to do that. What's hard is to take that improved integer regular expression pattern and publish it in a library so that other people can avoid making the same mistake. Of course, regular expression libraries do exist and they contain useful patterns like the email regular expression in which we saw before, but they really don't compose very well. The only way to compose regular expressions together is to concatenate the regular expression strings. This is because again, regular expressions are a whole domain specific language which is embedded in some other host language. Regular expressions don't have any of the composition features like functions and modules that we expect from general purpose programming languages. Monadic parsers on the other hand are just normal pure script functions and they compose very well. We've seen how to compose monadic parsers together by writing parser functions with parser combinators. Remember that the term parser combinator just means a function which takes some parsers as arguments and then returns a new parser. There are many other tricks we can do when we're using monadic parsers for pattern matching. There's one more trick that I want to mention in particular and that is matching recursive patterns. Regular expressions famously cannot parse an input string which has a recursive or tree-like structure. That means that regular expressions cannot parse HTML. They cannot parse JSON. They cannot match balanced parentheses. All of these things have a recursive tree-like structure. Here's a monadic parser named balance parens which matches a balanced group of parentheses. It will pair each open parenthesis with a closed parenthesis and capture the group when the last parenthesis closes. The way we write a monadic parser to parse a recursive structure like this is that we write a recursive monadic parser. You can see that the monadic parser calls itself on the second line and that is how it tracks its depth in the parenthesis parse tree. I'll read to you from the introduction to the paper Parsec Direct Style Monadic Parser Combinators for the Real World by Dan Lyon and Eric Meyer. Parser combinators have always been a favorite topic amongst functional programmers. Merge already described a set of combinators in 1975 and they have been studied extensively over the years by many others. In contrast to parser generators that offer a fixed set of combinators to express grammars, these combinators are manipulated as first-class values and can be combined to define new combinators that fit the application domain. Another advantage is that the programmer uses only one language, avoiding the integration of different tools and languages attributed to Hughes 1989. I want to repeat one of the key points about monadic parsers. They are written in normal pure script. Parsing input is such a hard problem that when people want to do it, they traditionally use a whole domain-specific language for parsing. The most famous domain-specific language for parsing is regular expressions, but there are many others. There has been a long tradition of general-purpose languages which are so weak that it's impossible to write anything in them which is slightly hard, like parsing an input stream. The whole point of the Perl programming language is that it's a weak, imperative style language with regular expressions built in so that we can use regular expressions for parsing input byte streams. That's the whole point of Perl, but it doesn't really solve the problem in a satisfactory way. When our computer program consists of two different languages, then we have the usual problem of language interoperation and the usual solution to language interoperation is to pass strings. A regular expression can capture a pattern in a string, but then it returns the captured pattern back to the host language as a string. And then what do we do with the string? We still have to turn the captured string into a data structure. Remember that we looked at the problem of extracting integers out of some input string. We wrote a regular expression which found integer-looking substrings and we ran it on the input string. The regular expression found some matches and returned the captured substrings. Suppose we then ran a string to integer conversion function on a captured substring and the string to integer conversion function failed. What then was the captured substring a legal integer string or wasn't it? In monadic parsers, we turn the string into a data structure in our native language during the parse. So there is no ambiguity about whether or not the input string was legal. In this case, the data structure is an integer. That's a very simple data structure, but it is a data structure. If we have a thing which is not an integer, then we can't represent it as an integer. So producing an instance of the integer data structure provides a proof that the input string was legal. Monadic parsers allow us to match patterns in normal pure script instead of using a domain-specific parsing language like regular expressions. And after we have matched a pattern, we produce a data structure which preserves the proof that the input string was legal. Here is a type for a monadic parser. This is an excerpt from the 1998 paper, Functional Pearls, Monadic Parsing and Haskell by Graham Hutton and Eric Meyer. On the bottom line, it says that the type of a parser for a data type A is a function from a string to a list of pairs of an A and a string. The simple type definition tells us pretty much everything we need to know about monadic parsers after we spend some time thinking about it. Like all good math, it has a simple definition, but complicated and far-reaching implications. Modern monadic parser libraries usually don't use this exact type definition for a parser, but they use definitions which are equivalent. The essay, Revisiting Monadic Parsing and Haskell by Veebab Sagar has some good discussion about that. And if that last definition was too prosaic for you, then here is the same definition expressed as a poem by Fritz Rohrer, Dr. Zeus on parser monads. A parser for things is a function from strings to lists of pairs of things and strings. Okay, that's enough theory. We'll stop at the Dr. Zeus level of parsing theory. We don't actually need to know any theory to use monadic parsers, but now you know that the theory exists and that the theory and techniques have been pretty well established since the 1990s. So in a JavaScript runtime environment, you can expect a pure script monadic parser to run at least 10 times slower than a JavaScript regular expression. That's just how it is. And that situation probably will not improve much in the future. If your input byte stream will be large and you need to process it quickly, then you might not be able to use pure script monadic parsers. But all of the techniques we talked about here are also used in Haskell. There are Haskell monadic parsing libraries such as Addo Parsack, which run very fast. About as fast as regular expressions, sometimes faster. So if you learn these monadic parsing techniques, then you can apply them in other execution environments very well. Whenever your process receives a byte stream from the world beyond the process input boundary, the first thing you should do is to parse that byte stream into a data structure. Monadic parsers are the easiest and most effective way to do that. Unless you already have a parsing library for the specific format of the byte stream or you are under severe performance constraints, the first technique you should consider for parsing an input byte stream is monadic parsers. Thank you very much for listening to my talk. Oh, here's a question from Toastal. Has the performance of the parsers in pure script improved recently? I tried to use a lib from CSV, but it crashed my browser because of pure scripts parser being slow. No, they haven't really improved and it's hard to improve them. I've been working on that. I've tried some different things, it's difficult. There are actually two monadic parsing libraries in pure scripts. There's pure script parsing, which is what I'm the maintainer of and which I've been talking mostly about today. There's also another one called pure script string parsers. The main difference with pure script string parsers is that it does not use monad transformers, which makes it faster. And so if you have performance problems, you can try using that one. It will run a little bit faster, but really regular expressions in a JavaScript execution environment are just gonna be faster because there's a whole just in time regular expression compiler that compiles regular expressions in JavaScript down to very fast code and runs them. And we don't have, there's no equivalent jit compiler for monadic parsers. So in general, monadic parsers don't have to be slower than regular expressions. They just, they are for historical reasons because Google and the WebKit developers have spent just years and years of programmer time and millions of dollars in programmer salaries optimizing regular expressions. And regular expressions in other libraries have been optimized for decades and decades as well. And they're super fast now. Monadic parsers can be fast. In many cases, they are fast. The Addo Parsec library is fast. Like I said in the talk, it can be as fast or faster than regular expressions. In JavaScript runtime environment, that situation is not gonna get better anytime soon. Okay, so let's go to another question. So James Collier asks, can monadic parsers also parse non-regular languages like context sensitive grammars? Yeah, it's a really good question. I wanna talk about that. Let me ask, let me answer James's other question first. So his question, so his other question is recursive monadic parsing isn't complicated by strict evaluation? It is not, it is not. It's not complicated by strict evaluation. That's a really good question though. It's a very, very good thing to ask, especially when you're coming from Haskell into PureScript, sometimes you get bitten by strict evaluation. But in general, no, it's gonna be fine. The place where it would really bite you is with the alternative operator. If it were going to, if you don't want your, you don't want because alternative operator is a function and PureScript is strict in evaluating. It's in for functions, which means it will fully evaluate all the arguments before it calls the function. And so if you have an alternative operator which fully evaluated the left parser and fully value out of the right parser and then, you know, called the function, like wouldn't that mean that both parsers get evaluated? Well, no, it doesn't because it fully evaluates the parser, but it doesn't run the parser. So all the semantics of monadic parsing in PureScript work exactly how you would expect them to work if you're coming from a lazy language like Haskell and Haskell monadic parser libraries. Another question. Oh, your other question was, can monadic parsers also parse non-regular languages like context-sensitive grammars? So let me get back to that in just a second. Let me see what other questions there are. In Raku, you can have recursive regex patterns. Yes. And, oh, yeah, right, sir. And that's an extension, but it's an extension that makes the regex parser not regular. So like, you're getting none of the advantages, like you're losing a lot of the advantages of regular expressions that they can be equivalent to a finite state automata, but then you're getting the disadvantages of regular expressions, which is the syntax. So like, yeah, you kind of get worse of both worlds with recursive regular expressions. Grammars are interesting too. In Raku, that's cool. Thanks for the link. I'm not familiar with Raku. Does that mean we can mix in regex for speed? Yes, you can mix in regex for speed, especially the PureScript string parsers library in PureScript has a regex parser. And I have an issue on PureScript parsing because I wanna add a regex parser into PureScript parsing so that you can call regex in the middle of a monadic parser. It's a really good feature. You can do that in PureScript string parsing. You'll be able to do it in PureScript parsing very soon. What about a monadic parser combinator library that compiles to regex from Joseph Young? That is, so there's a Haskell library which is called applicative regex. No, regex applicative. It's called regex applicative. It is super, super interesting. I can't remember the name of the author off the top of my head. But basically what it is, is it's a regular expression library, but it's written in terms of applicative combinators in Haskell. So it's not monadic, instead it's applicative. Applicative is a slightly weaker algebraic structure which is kind of like a monad. But in applicative computations, you know the shape of the computation in advance as you do with regular expressions. So you actually don't need a full monad if you're just doing regular grammars. You can use an applicative and that's what the regex applicative is all about. And that library is very, very interesting. You get all the nice syntax and composability of a monadic parser library, but you get a parser's regular grammars. Why do all parsers have that byte to, so T-Rex, why do all parsers have that byte to tuple type? Because, I'm not sure what you mean by that. A lot of the examples I used in the talk were the care parser which parses one byte. Could you rephrase that question, please? I'm not sure you mean by that. Severe performance constraints. Yeah, okay, I talked about that. So, okay, I wanna talk about, I wanna go back to James Collier's other question about if a monadic parser can parse a context sensitive grammar. What does he mean by that? He's talking about, oh, let me share my screen here. Can you see this, am I sharing? Yes, this is sharing. Okay, he's talking about, really what he's talking about is this, the Chomsky hierarchy. Everybody here familiar with Noam Chomsky? This guy, he's super famous. And this is what he is famous for. The Chomsky hierarchy provides a hierarchy of grammar complexity. And regular expressions can parse, yes, Noam Chomsky. Regular expressions can parse regular languages which are type three, the weakest level on the Chomsky hierarchy. Monadic parsers can parse the next level up, type two, context free grammars. Because they're normal functions, they can be recursive, they can call themselves like any normal function in PureScript. And because they're recursive, then they are effectively the same as a non-deterministic push down automaton. A push down automaton is just another name for the data structure known as a stack where you can push things and then pop things. And your call stack becomes the data structure which provides the push down automaton capability and then you get type two. So you can parse type two, context free grammars. Now there's a way to get to type one with monadic parsers too. And that is through monad transformers which is what Jordan's talk was about. If you have a parsing library, I didn't talk about this during the talk but all the parser types in PureScript parsing are monad transformers. And you can add, which means you can add new monad capabilities to your parsing monad. And you can, which means you can bring in any context into the computation that you want. Any kind of monadic capability like a state monad, you can transform your parser monad with a state monad and then you can bring in some state context into your computation. And then you can use that context that you've brought in to make decisions about your parsing. And that makes your parsing context sensitive and then you can parse type one grammars using monadic parser combinators. Type one grammars include most computer programming languages for instance. So if you want to parse a computer programming language you will need a parser that can parse type one grammars and monadic parser combinators with monad transformers can do that. Any other questions? Oh, T-Rex question. Type parser, a string equal, parser is a function from strings to lists in pairs of strings and things. You said this always holds true and is proven. Why is it true? It's true, it's not, it's not exactly a theorem. It's a type. And so that if you have, so this type it's this type if you have a parser which has this type then it gives you all the capabilities that you need for a parsing monad. It gives you alternative failure and parsing state. So that's why that's a parsing monad. And like I said during the talk that's not actually the exact type which is used in which is used in pure script parsing but it uses a type which is equivalent. And the best discussion of this is Veebh Segar's essay revisiting monadic parsing in Haskell. It's a really good discussion for this question. Aditya says for a moment I was wondering when Noam Chomsky became a mathematician. Yeah, he kind of is a mathematician. The reason he's so famous, the reason he's the most cited scholar living alive today and the most famous public intellectual in America is because he brought this mathematics, this mathematics of computational logic into the field of linguistics and applied it and began answering questions which no one had ever been able to come up with good answers before. Noam Chomsky actually thinks that the difference between humans and animals is that human brains are capable of, are essentially Turing machines that they have a grammar generation organ which is subject to the fundamental theorem of computer science which is, you know and that's really what Noam Chomsky is all about. That's why he's super famous. Yeah, you should go down to Wikipedia rabbit hole about that, it's super interesting. Let me show you some stuff here. So, let me show you an example of what it looks like to pull in context. Actually, I don't know, maybe my question and answer session is going over here. Aditya, tell me if you want us to go to the next talk, okay? So, this is from the documentation for my Haskell megaparsec version of the replacing, the parsing replace library. This replace library is a library that I've written three times and I alluded to it in the talk. It's not part of the basic parsing library. It's not part of the basic monadic parsing library. It's an add-on library which provides some extra features. I basically wrote this library by going through the Python RE module which is really good and it's about regular expressions and provides all the features that you can do for regular expressions in Python. And it's really ergonomic and really useful. And so I just went through this whole library and made sure that everything you can do with regular expressions in Python you can also do with Haskell or PureScript. Every time I found something that you couldn't do then I added it to this family of libraries that I wrote and published it. There's three of them. There's PureScript parsing replace and then there's also Haskell libraries replace megaparsec and replace adoparsec for those parsing libraries. And so from the replace megaparsec library this shows you how you can pull in a state context into a monadic parsing operation in order to provide in order to parse context-sensitive grammars in order to solve context-sensitive parsing problems. And here, and as you maybe remember from Jordan's talk you apply a monad transformer and you pull in a state and then you just stack your parsing you use that as a base monad for your parsing monad and then you have the state available to you while you're doing the parsing computation. And this allows you to solve this allows you to do the same thing that you would in parsing resubn. So in Python resubn it performs the same operation as subn and returns a tuple which tells you the number of substitutions that you made. So what is sub then sub is search and replace it searches through a string replaces everything but you can also tell it to stop after a little while and you can say you only wanna do a certain number of substitutions and stop after that and then report back after you've made the substitutions. And in Python, they had to provide a whole function in the library which is this resub function or actually the resubn function. But with these monadic parsing libraries with monad transformers we don't need to provide a library function we don't need to add anything to the API. This is just an example which shows you how to do it because monad parser transformers already give you all the capabilities you need to do an operation like that you just have to but it helps to see an example it's hard to figure it out if you've never done it before. So there's an example of that provided in replace mega parsing and there's also an example on the pure script in the pure script parsing replace library. So I think I'm gonna stop there that's all I have to say did I miss any questions while I was talking? Oh how easy is it how easy is it to parse binary data in pure script? Great question. Super easy, super easy on the pure script parsing library go down here related packages, pure script parsing data view if you wanna parse binary array buffers you can use this library here which goes with pure script parsing. Okay before we move on just there's actually a question that James I think you're gonna have to take this question James Collier was asking regarding Jordan's talk and one second. Yes, James Collier was asking on YouTube regarding Jordan's talk is the state T transformer related to the ST monad? Uncle? Is the state T monad transformer related to the ST monad? Oh, that's sorry that's a monadic transformer talk. Jordan do you wanna take that one? You can take that one too, James. Okay, no it's different that's confusing because they sound the same but they're different monads. ST monad is like an effect monad for when you just wanna have some local effects and do some mutation. ST monad is basically a performance trick that you can do if you have an algorithm you wanna do for and you wanna do some local mutation in a little localized area like an isolated localized area in your program where you do mutation and then you get the result out of that that's what the ST monad is for and then the state T monad is a monad transformer for carrying state along in a monadic computation.