RowList Fun with Purescript

3b48c91bf6b6f0bfd0fda50625598656?s=47 Justin Woo
September 01, 2017

RowList Fun with Purescript

A small talk on RowList and some type-level programming in Purescript given at Small FP Conf 2017 in Tampere.

Reddit thread:


Justin Woo

September 01, 2017


  1. RowList Fun with PureScript Justin Woo Small FP Conf 2017

  2. What is PureScript? • Pure, Strict, Functional • Higher Kinded

    Types • Type classes with functional dependencies • Tooling with purescript-ide server + client • Docs for packages on Pursuit • Easy-to-use build tool in Pulp • Typed holes with type-directed search • No runtime -- compiles to real JS • Row types • ...and more!
  3. What are Row types? • An unordered collection of fields

    ◦ Key is Symbol (a type-level string kind) ◦ Value is of the kind provided to the row data Record :: # Type -> Type -- takes row of Type to define a Type type Person = { name :: String, age :: Number } type Person = Record (name :: String, age :: Number) • Eff works this way! kind Effect data HTTP :: Effect data Eff :: # Effect -> Type -> Type main :: Eff ( http :: HTTP ) Unit
  4. Collection? How about as a List? • What if we

    could use these rows as lists of fields? Wouldn’t that be fun? kind RowList data Nil :: RowList data Cons :: Symbol -> Type -> RowList -> RowList type PersonRow = (name :: String, age :: Number) type PersonRowList = (Cons "name" String (Cons "age" Number Nil))
  5. Type classes: “type-level case statements” • How do you match

    and work with things on the type level? Type classes! ◦ Using instances for each branch • Simple example: getting keys from a record class Keys (xs :: RowList) where keysImpl :: RLProxy xs -> List String instance nilKeys :: Keys Nil where keysImpl _ = mempty -- Phantom types reminder data Maybe a = Nothing | Just a data Proxy a = Proxy data RLProxy (xs :: RowList) = RLProxy
  6. Cons instance of class Keys 1. Pattern match on the

    RowList 2. Reflect the Symbol to a string value 3. Get the rest by calling keysImpl on the tail 4. Cons this string onto the rest of the keys That’s it! instance consKeys :: ( IsSymbol name , Keys tail ) => Keys (Cons name ty tail) where keysImpl _ = first : rest where first = reflectSymbol (SProxy :: SProxy name) rest = keysImpl (RLProxy :: RLProxy tail)
  7. Exposing our method in a reasonable way • We wrote

    a class for RowList, but this needs to work for row types from Records • RowToList allows us to convert row to RowList for exactly this! keys :: forall row rl. RowToList row rl => Keys rl => Record row -> List String keys _ = keysImpl (RLProxy :: RLProxy rl) • Since we have everything from the type level, the record ends up being a glorified Proxy
  8. Keys in action Usage: main = traverse_ print $ keys

    { a: 1, b: 2 } Output: "a" "b"
  9. • Purescript-Simple-JSON: automatically parse record type aliases without any setup

    ◦ Also writes JSON safely (e.g. no function stringifying) • Purescript-Kancho: restrict Purescript types to Elm port types and generate Elm types • Purescript-OhYes: restrict Purescript types to be Typescript-safe and generate type definitions, using polymorphic variants through Purescript-Variant to support discriminant-field union types in Typescript • Purescript-ChocoPie: a Cycle.js-like library that exposes record-based APIs for creating cycles of Events from Purescript-Behaviors • Purescript-IndexedDB-Safe by Antti Holvikari: type-safe wrappers for IndexedDB • … and more! Other applications of RowList
  10. “Will I need to do this if I [use /

    start using] Purescript?” • Writing type-level code using RowList? ◦ Not really, but sometimes ◦ And you will learn these from usage • Defining your own type classes and working with them? ◦ Not necessarily, but sometimes ◦ And you will learn these from usage • Understanding how to use constraints and throw them around? ◦ Yes and no ◦ You will learn these from usage ◦ You can always start with the most concrete types and use more general ones over time ▪ e.g. Monoid mempty vs []
  11. Thanks! Ask me Purescript questions on Twitter: @jusrin00 Map/Zip/Keys examples

    on IRC: #purescript on freenode Slack: #purescript on Functional Programming Slack Reddit: /r/purescript