Slide 1: Getting Started With PureScript
- Image credit: http://crowactive.com/wp-content/uploads/2010/05/crow6sm.jpg
- We'll take a quick tour of the language and see what makes it special. Then, we'll see how we can start using it and learning about it in depth.
Slide 2: Literals
- Functional programming languages are concerned with values. These are the values we use in PureScript. For those of you with a Haskell background, most of the syntax and many of the concepts will be pretty familiar, so bear with me. A notable difference to keep in mind is that PureScript is strict, whereas Haskell is lazy.
- The double-colon syntax specifies the type of each value. We'll be including explicit type signatures throughout these slides, but none of them are needed -- the PureScript compiler can almost always infer the type of each value. Though, it is convention to include explicit type signatures with each top-level declaration.
Slide 3: Function Definition & Application
- Defining functions in PureScript is very easy. The sum function expects two parameters,
b, and returns their sum.
- When a PureScript function is given fewer arguments than it's expecting, it'll return a function that expects the remaining arguments. This is called auto-currying. The successor function takes advantage of this by being defined in terms of sum.
- And of course, you can fully apply a function as we do in the definitions of
Slide 4: Function Definition (cont.)
- Functions don't have to be defined in terms of concrete types. The type of the return value of some functions is determined by the type of its parameters. This is called parametric polymorphism.
identityfunction simply returns whatever it's given.
constfunction returns its first parameter regardless of the value or type of its second parameter.
$is an infix function, which means it's written between its two arguments when it's applied. The
infixrdeclaration below declares that it associates to the right (r) with a very low precedence (0).
Slide 5: Records
birth yearfields, and my
- We can write a function that can say hello to either of these records because they have a structural similarity. We read the type signature of
greetas "a function that takes a record with a name field and zero or more other fields called
r, then produces a string". We call
ra "row of fields", and this is called row polymorphism, one of PureScript's most notable features. In PureScript, type variables like
rare always explicitly universally quantified, unlike in Haskell.
hankJris defined in terms of
hank, using a record update. This does not mutate
hank. It can be thought of as an object with just a
birth yearown-property that has
hankas its prototype.
Slide 6: Algebraic Data Types
- In addition to the primitive data types, PureScript allows you to define your own data types. The
datakeyword introduces an Algebraic Data Type (or ADT).
- The Ordering ADT has exactly three possible values (or inhabitants): Less, Equal, and Greater. We can use those values just as we do values of any other type. Here, the
comparefunction returns an Ordering by returning one of those values.
Slide 7: Algebraic Data Types (cont.)
- An ADT can be parameterised by another type.
Listis parameterised: when given a type
t, it produces a type. EmptyList is an inhabitant of any List type.
Consis what we call a type constructor. It can be applied like a function to a value of type
tand another value of type
List tto produce a value of type
List t. That may have been a bit confusing, so let's look at the examples.
listAis a value of type
List Number. Remember, that is a single type. In this case,
Consmust be applied to a
listBcreates a similar structure, but with
Strings instead of
listCis a member of any List type because EmptyList doesn't mention the type parameter
tin its definition.
Slide 8: Type Aliases
- Type aliases are a very simple feature.
- Maybe is aliased as Option. An Array of Numbers is aliased as NumberArray. List Number is aliased as NumberList. And a record with numeric
yfields is aliased as Point.
- And just like ADTs, type aliases can be parameterised. Pair is shorthand for a record with left and right fields, and Predicate is an alias for any function that returns a Boolean.
Slide 9: Pattern Matching
- Pattern matching is an extremely powerful feature. In a parameter list, wherever we would use a variable, we can instead use a pattern. When a program has multiple consecutive definitions, failed pattern matches fail through to later definitions, kind of like switch-case in C-like languages.
- In the definition of map, we can see a pattern match on the value Nothing. In that case, the function returns a Nothing value. When the second parameter is not a Nothing, we know it must be a Just, and we can destructure the value out of the constructor.
map-primebelow is the same function, but implemented using pattern matching in a
caseinstead of the parameter list. Pattern matching in a parameter list is only sugar for pattern matching in a case expression in the function's body.
Slide 10: Pattern Matching (cont.)
- Fields of a record can be pattern matched.
doubleZero's first definition matches a record with an
xvalue of 0 and binds
yto the record's
pto the record itself using an at-pattern. If that pattern succeeds, it'll return a record with the
yvalue doubled. Otherwise, execution will fall through to the second definition, which simply returns its argument.
- And finally, the underscore in
constallows us to ignore a parameter without binding it to a name.
Slide 11: Program Structure
- A typical PureScript program is split into one module per file.
- The module name is specified at the top, followed by imports, type declarations, and then value declarations.
Slide 12: Program Structure (cont.)
- If you're like me, you're going to want to be a bit more explicit about what you import and export.
- Explicit exports may be listed after the module name, and explicit imports after the imported module name.
- Use the
qualifiedkeyword to import a whole module under a given name.
- Modules that are intended to be executable will export a special function named main. This particular main function has no effects.
Slide 13: Hello, World!
- Even though it's a bit verbose, this would be an idiomatic hello world program.
Slide 14: Effects
- What makes PureScript "pure" is that it tracks effects in the type system.
- The Eff constructor pairs a row of effects with a type. When an Eff is evaluated, a value of that type is produced, and the effects can be observed.
- For instance, the
randomvalue includes the RANDOM effect, which indicates that it affects the random number generator, and produces a Number.
logis a function that generates an Eff from a String. The resultant Eff includes the CONSOLE effect, which means that it will print to the console, and produces a Unit value. Unit values are used to indicate that the effects are the primary goal of the function. This function is similar to the console.log you already know, which is evaluated only for its effects and always returns null.
log, but it's able to converts its argument to a String first.
mainfunction composes two effects using this operator that's pronounced
bind. The important thing to know about this operator is that it passes the random number that was generated when evaluating
randomas the input to the
Slide 15: Composing Effects
- When we want to compose additional effectful functions, it's convenient to use do notation. This makes a sequence of effectful function calls look more imperative.
- Remember that bind passes along the generated value to the following function. This
dosugar allows us to save that value to a reference using the leftward facing arrow, which is pronounced "gets". In this case, we do that with
- When the value is a Unit, we choose to just ignore it by omitting the arrow.
Slide 16: Type Classes
- Type classes are an unfortunately named feature, but they're incredibly useful for code re-use, enabling ad-hoc polymorphism. This means that functions can be defined in terms of the type classes that a type implements, much like how interfaces are used in programming languages that have interfaces.
- This is a type class in the standard library called Show. Any type
athat implements Show must provide the definition for a
showfunction that can generate a string for each of its values. This type class is what allowed the
- Boolean implements Show very simply using a piece-wise definition: return the string "true" for true and the string "false" for false.
- Arrays can implement show as long as their parameter implements Show. The part of the type signature before the fat arrow is called a constraint. This one requires
ato implement Show. This is necessary because in the recursive case we need to call
x, which is an
- Use the
foreignkeyword to import in-scope names. You'll need to provide an appropriate type signature for each foreign value.
parseFloatis a function from String to Number.
- Remember, since PureScript curries its functions, we need to return a new function for each argument. So
powtakes a base, then returns a function that takes the exponent and returns the result.
- We can also depend on foreign types and foreign effects. Here we're importing the values
clearTimeoutalong with the effects and types they need. Note that the names we choose for the foreign types and effects are arbitrary.
setTimeouttakes an effectful function with a meaningless return value and a delay in milliseconds, and returns a value that represents both the effect that setTimeout has on the event loop and the unique identifier for this event.
clearTimeouttakes one of those unique TimeoutID identifiers, and returns a value that represents the effect it has on the event loop.
Slide 19: Installation
- So at this point, you can't wait to start using PureScript right now.
- You can either grab a pre-compiled binary off Github or install it through one of these three package managers.
Slide 20: Starting a Project
- To get a project started, I recommend using pulp, which is a project management tool designed specifically for PureScript.
- You can also use the Yeoman generator, which will create a Gulpfile along with the basic project structure.
- And of course you can just create the source tree and build files yourself.
Slide 21: Building
- Building with pulp and gulp is easy.
- If you're doing it yourself, you'll need to use the psc and psc-bundle binaries directly.
- psc compiles all the PureScript and FFI files for your project and your dependencies.
Slide 22: Managing Dependencies
- Dependencies are managed through bower. You can get bower through npm.
- Use the --save flag when installing runtime dependencies, and the --save-dev flag when installing development dependencies.
- To publish, you'll need to push up a git tag. You only have to register with bower the first time you publish. Publishing further versions is as easy as pushing a new git tag.
Slide 23: Testing
- Before you publish, you're going to want to make sure your library is well tested. We're going to write some property-base tests.
- It's a convention to define our tests in the
Testnamespace. We import our testing library, QuickCheck. We're going to test some invariants about the Data.Array.sort function.
- The first invariant is idempotence: sorting an array twice should be the same as sorting it once.
- The second invariant is length preservation: sorting an array should not change its length.
- We export a main function that executes these tests in sequence.
Slide 24: Testing (cont.)
- First we make sure we have the quickcheck dependency installed using bower install, then we can run our tests with pulp.
- QuickCheck will run 100 random inputs through each of our tests, and let us know if any of our invariants have been violated. These 100 random inputs could easily be a million by just telling QuickCheck how many tests you want.
Slide 25: Unit Tests
- For those of you that prefer making individual assertions about your program's results, purescript-spec provides a familiar unit testing interface.
- Quickcheck tests can be run alongside the unit tests using a purescript-spec extension.
- The purescript-spec test runner also supports running tests in a browser environment.
Slide 26: PSCI
- An easy way to get started playing around with PureScript, once you have it installed, is through PSCi, the PureScript REPL.
- Here, you can evaluate PureScript expressions, browse your loaded modules, and even ask the compiler to show you the types it infers for the expressions you enter.
Slide 27: Pursuit
- Pursuit is a search engine and package browser for PureScript libraries.
- I find it particularly useful for quickly looking up functions by their type signatures.
Slide 28: PureScript wiki
- The PureScript wiki has detailed information about each individual feature of the language. I recommend reading the FFI guide for some helpful FFI tips.
- If you need help with anything, the community is pretty active on the #purescript IRC channel on freenode.
Slide 29: purescript-demo-mario
- If you want to see everything put together into a real program, I recommend you look through the source of this very simple Mario game.
- It allows you to run and jump as Mario in a web browser, and it's written entirely in PureScript & HTML.
- It's intended to be both a beginner PureScript learning resource and an example of how to use PureScript in the browser.
Slide 30: purescript-express
- Once you've done that, I recommend you play around with the PureScript adaptor for the Express web framework.
- It comes with a decent example, and should allow you to actually build something useful at the same time.
Slide 31: purescript-node-webkit
- And we can even make desktop apps entirely in PureScript with nw.js through the purescript-node-webkit adapter.
Slide 32: PureScript by Example
- If you want to go in-depth with PureScript, and learn more about functional programming patterns in general, check out PureScript by Example on Leanpub.
- It was written by Phil Freeman, the original author of the PureScript compiler.
Slide 33: Big Changes in 0.7.x
- And lastly, don't mistake my excitement for PureScript with an endorsement to go out and start running it on your pacemakers. It's still quite young and under heavy development.
- There are many big changes in these early pre-1.0 releases. Some features we're looking forward to in 0.7.1 and beyond: generic deriving, exhaustivity checker, improved error messages and warnings, JVM/C++11 backends, parallel builds, provenance for type variables in errors, ECMAScript 6 in FFI, and data kinds. One of the notable features PureScript is missing is constraint inference, so if you're writing polymorphic functions with constraints, you'll need to explicitly annotate them.
Slide 34: Conclusion