Composable Parsing: Map

Episode #59 • May 27, 2019 • Subscriber-Only

We now have a precise, efficient definition for parsing, but we haven’t even scratched the surface of its relation to functional programming. In this episode we begin to show how all of the functional operators we know and love come into play, starting with map.

Map
Introduction
00:05
Recap
02:02
Transforming parsers
04:36
Defining map on Parser
05:47
Map’s universal property
10:07
Mapping new parsers
14:23
Map’s limitations
20:09
Till next time
22:33

Unlock This Episode

Our Free plan includes 1 subscriber-only episode of your choice, plus weekly updates from our newsletter.

Introduction

In the last three episodes (part 1, part 2, part 3) we set the stage for parsing.

First we showed that parsing is such a common and important task that Apple provides multiple solutions for parsing in both Swift and Foundation. Everything from initializers that will parse a string into a specific type, such as integers, doubles, UUIDs and URLs, to general purpose parsers like the Scanner type, which allows us to incrementally parse various types off the beginning of strings, to regular expressions.

All of those parsers are handy and powerful, but they have some serious drawbacks, such as they are not really defined for code reusability or composability. So we took a step back, and provided a precise definition of what a parser is. It was literally a function that takes an in-out substring as input and returns an optional, first class value as output.

With that simple definition we had a succinct, efficient description of parsing, and we even made a few domain-specific parsers that did just a little bit of parsing, but did it well. Then we pieced those parsers together and made a pretty complicated parser yet the code was very descriptive and straightforward. It even fixed some subtle edges cases that a hand rolled parser had missed.

But even though we accomplished a lot in the last 3 episodes, it doesn’t even scrape the surface of what functional programming has to say about parsing. Today we’ll really start to dig into that topic by seeing precisely how one glues together parsers to form ever more complex parsers. Once we unlock a few of the basic shapes we will be able to create incredibly powerful parsers with very little work, and the sky will be the limit!

This episode is for subscribers only.

Subscribe to Point-Free

Access this episode, plus all past and future episodes when you become a subscriber.

See plans and pricing

Already a subscriber? Log in

Exercises

  1. Generalize the char parser created in this episode by turning it into a function func char: (CharacterSet) -> Parser<Character>. Use this parser to implement the northSouth and eastWest parsers without needing to use flatMap.

  2. We have previously devoted 3 entire episodes (part 1, part 2, part 3) to zip, and then 5 (!) entire episodes (part 1, part 2, part 3, part 4, part 5) to flatMap. In those episodes we showed that those operations are very general, and go far beyond what Swift gives us in the standard library for arrays and optionals.

    Define zip and flatMap on the Parser type. Start by defining what their signatures should be, and then figure out how to implement them in the simplest way possible. What gotcha to be on the look out for is that you do not want to consume any of the input string if the parser fails.

  3. Use the flatMap defined in the previous exercise to implement the northSouth and eastWest parsers. You will need to use the always and never parsers in their implementations.

  4. Using only map and flatMap, construct a parser for parsing a Coordinate value from the string "40.446° N, 79.982° W".

    While it’s possible to solve this exercise, it isn’t particularly nice. What went wrong, and what other operation could you use to make it simpler?

References

Combinators

Daniel Steinberg • Friday Sep 14, 2018

Daniel gives a wonderful overview of how the idea of “combinators” infiltrates many common programming tasks.

Just as with OO, one of the keys to a functional style of programming is to write very small bits of functionality that can be combined to create powerful results. The glue that combines the small bits are called Combinators. In this talk we’ll motivate the topic with a look at Swift Sets before moving on to infinite sets, random number generators, parser combinators, and Peter Henderson’s Picture Language. Combinators allow you to provide APIs that are friendly to non-functional programmers.

Parser Combinators in Swift

Yasuhiro Inami • Monday May 2, 2016

In the first ever try! Swift conference, Yasuhiro Inami gives a broad overview of parsers and parser combinators, and shows how they can accomplish very complex parsing.

Parser combinators are one of the most awesome functional techniques for parsing strings into trees, like constructing JSON. In this talk from try! Swift, Yasuhiro Inami describes how they work by combining small parsers together to form more complex and practical ones.

Learning Parser Combinators With Rust

Bodil Stokke • Thursday Apr 18, 2019

A wonderful article that explains parser combinators from start to finish. The article assumes you are already familiar with Rust, but it is possible to look past the syntax and see that there are many shapes in the code that are similar to what we have covered in our episodes on parsers.

Sparse

John Patrick Morgan • Thursday Jan 12, 2017

A parser library built in Swift that uses many of the concepts we cover in our series of episodes on parsers.

Sparse is a simple parser-combinator library written in Swift.

parsec

Daan Leijen, Paolo Martini, Antoine Latter

Parsec is one of the first and most widely used parsing libraries, built in Haskell. It’s built on many of the same ideas we have covered in our series of episodes on parsers, but using some of Haskell’s most powerful type-level features.

Parse, don’t validate

Alexis King • Tuesday Nov 5, 2019

This article demonstrates that parsing can be a great alternative to validating. When validating you often check for certain requirements of your values, but don’t have any record of that check in your types. Whereas parsing allows you to upgrade the types to something more restrictive so that you cannot misuse the value later on.

Ledger Mac App: Parsing Techniques

Chris Eidhof & Florian Kugler • Friday Aug 26, 2016

In this free episode of Swift talk, Chris and Florian discuss various techniques for parsing strings as a means to process a ledger file. It contains a good overview of various parsing techniques, including parser grammars.

Downloads