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!
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
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))
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
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)
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
● 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
“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 []
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