"An elegant weapon for a more civilized age."
This talk assumes basic knowledge of Haskell syntax.
- Purely functional programming language
- Haskell syntax with improvements
- compiles to JavaScript
Numerous download options available via PureScript's website, including Linux package managers, NPM, Cabal, or compiled binaries.
For local projects everything can be done via Node.js:
mkdir purescript-example && cd purescript-example
npm install -g purescript
Test that you have a working install via psci
.
Uses tools familiar to modern JavaScript developers:
- npm to manage tooling
- grunt or gulp for compilation and tasks
- bower for project package management
For a quick start, clone the associated example project and let's have some fun!
Install all required dependencies...
npm install
bower install
grunt
...and open index.html
in your browser of choice. If you
open the console, you should see "Hello, World!" output.
- see the
<script>
and<body>
tags inindex.html
. - see the corresponding
main
function ofsrc/Main.purs
psc
provides a very straightforward mapping between
written PureScript and generated JavaScript
.
- straightforward mapping of PureScript to JavaScript
- native JavaScript types
- compilation provides type-checking, dead code elimination, some optimization
- higher-kinded types
- type-classes
- together, this means monads!
- higher granularity of type-classes
- strict by default
- uses common tooling
- can be easily added into existing projects
All of the pure functions and immutable data that we know and love, but improved upon.
head :: [a] -> Maybe a
head (x:xs) = Just x
head _ = Nothing
- allows for predictable interop between JS and PS
- includes
String
,Number
,Boolean
,[]
,Object
Object
!? Like Haskell records but better!
Example of a PureScript object literal (of kind #
):
type Person = Object (name :: String, age: Number)
myself :: Person
myself = { name: "Jon Childress", age: 32 }
getAge :: Person -> Number
getAge person = person.age
agePlusOne :: { age :: Number | rows } -> Number
agePlusOne record = record.age + 1
Note: function composition operators are <<<
and >>>
.
Rather than IO, carries side-effects in "Eff" monad that
tracks "rows" of effects much like an object (of kind !
)
main :: forall eff. Eff (trace :: Trace | eff) Unit
main = trace "Hello, World!"
Example from purescript-number-format library:
errorAsNothing
:: forall e a. Eff (err :: Exception | e) (Maybe a)
-> Eff eff (Maybe a)
errorAsNothing = catchException (return <<< const Nothing)
toString :: Number -> Number -> Maybe String
toString radix num =
runPure
(errorAsNothing $ Just <$> unsafeToString radix num)
Functor, Semigroupoid, Semiring, Apply, Bind, Applicative, Plus, Alternative, Monad, MonadPlus, etc...
- allows for finer type constraints
- each class handles a single behavior
- makes understanding each class easier
class (Monad m, Alternative m) <= MonadPlus m where
class (Applicative f, Plus f) <= Alternative f where
- allows for predictable interop with JavaScript
- easier to reason about resource behavior
Control.Lazy
allows for deferring evaluation
foreign import stringify """
function stringify(n) {
return function (x) {
return JSON.stringify(x, null, n);
};
} :: forall a. Number -> a -> String
"""
import Data.Function
foreign import stringify """
function stringify(n, x) {
return JSON.stringify(x, null, n);\
}
""" :: forall a. Fn2 Number a String
stringify :: forall a. Number -> a -> String
stringify n x = runFn2 stringify n x
The V validation monad provides helpers for collecting
errors in arbitrary Semigroup (monoid sans mempty
).
validate :: Person -> V [Error] Person
validate person = { first: _, last: _, email: _ }
<$> validateName person.first
<*> validateName person.last
<*> validateEmail person.email
The C continuation monad helps wire together asynchronous calls in a predictable manner.
copyFileCont :: forall eff. FilePath -> FilePath -> C eff (Either ErrorCode Unit)
copyFileCont src dest = do
e <- readFileCont src
case e of
Left err -> return $ Left err
Right content -> writeFileCont dest content
The RWS monad allows for all operations allowed by
StateT
, ReaderT
and WriterT
transformers.
Each transformer is defined in terms of type-classes, so that nested monads receive each other's functions.
The Parallel applicative wraps the continuation monad to provide parallel computation.
loadModel :: ContT Unit (Eff (ajax :: AJAX)) Model
loadModel = do
token <- authenticate
runParallel $
Model <$> inParallel (get "/products/popular/" token)
<*> inParallel (get "/categories/all" token)
Signals provide a means of reacting to user input in
a predictable form. Signals include key-presses, mouse
events, and even requestAnimationFrame()
.
Signals are beyond the scope of this talk, but see the Elm signal documentation for a great explanation.
- "standard library" modules found in purescript org
- curated contributions in purescript-contrib org
Read the fantastic PureScript book to get up-to-speed with this amazing language.