Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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: https://www.reddit.com/r/purescript/comments/6xs5f2/rowlist_fun_with_purescript_slides_from_small_fp/

3b48c91bf6b6f0bfd0fda50625598656?s=128

Justin Woo

September 01, 2017
Tweet

More Decks by Justin Woo

Other Decks in Programming

Transcript

  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 github.com/justinwoo/purescript-map-record IRC: #purescript on freenode Slack: #purescript on Functional Programming Slack Reddit: /r/purescript