Some info that supposed to help to understand PureScript from Haskell perspective.
If you already know js it will be even simplier.
PureScript written in Haskell but usually distributed as binaries via NPM.
It uses Bower instead of NPM because Bower
have flat dependencies and better dependency resolution.
This explained better here: http://harry.garrood.me/blog/purescript-why-bower/
PureScript is strict by default!
- https://pursuit.purescript.org (kinda like https://stackage.org for Haskell)
- http://try.purescript.org (online REPL)
- #purescript on Freenode
Matrix bridge: https://riot.im/app/#/room/#freenode_#purescript:matrix.org - https://gitter.im/purescript/purescript
Matrix bridge: https://riot.im/app/#/room/#gitter_purescript=2Fpurescript:matrix.org
-
About
Prelude
:PureScript acts like
{-# LANGUAGE NoImplicitPrelude #-}
in Haskell, andPrelude
also isn't distributed with PureScript compiler.You need to install dependency
purescript-prelude
and to import it:import Prelude
-
About
forall
:PureScript acts like
{-# LANGUAGE ExplicitForAll #-}
in Haskell.You need to explicitly declare
forall
for every polymorphic type variable. -
About unicode:
In PureScript using unicode is allowed by default.
Basic unicode symbols also included (such as
∷
,←
,→
,⇐
,⇒
,∀
, etc.) -
Basic operators equivalents (those which differ):
PureScript Haskell (<<<)
(.)
(>>>)
flip (.)
or(.>)
fromflow
package(#)
(Data.Function.&)
frombase
package(<#>)
(Data.Functor.<&>)
frombase
package(<>)
(Semigroup
type class)(++)
a *> b
(Apply
type class)a >> b
b <* a
(Apply
type class)b << a
-
Basic functions equivalents (those which differ):
PureScript Haskell map
(Functor
type class)fmap
(works likemap
for lists)unsafeThrow
fromControl.Monad.Eff.Exception.Unsafe
frompurescript-exceptions
packageerror
forkAff
fromControl.Monad.Aff
frompurescript-aff
packageforkIO
If you're looking for Haskell's
Control.Concurrent.MVar
look at PureScript'sControl.Monad.Aff.AVar
frompurescript-aff
package. -
About point-free style:
For partially applied operators you must specify ghost place for a value:
PureScript Haskell (_ + 2)
(+ 2)
(2 + _)
(2 +)
You're defenitely familiar with
{-# LANGUAGE LambdaCase #-}
in Haskell:In PureScript you have kinda the same, but again, you need to explicitly set ghost place for a value:
case _ of Just x -> 34 Nothing -> 42
That in Haskell would be:
\case Just x -> 34 Nothing -> 42
To update a record in PureScript you also use a ghost place marker:
_ { foo = 42 }
But in PureScript you also able to easily modify nested records without even using stuff like lenses:
_ { foo { bar { baz = 42 } } }
You able create a function that fills record values this way:
{ foo: _, bar: _ }
Which is equivalent to:
\foo bar -> { foo: foo, bar: bar }
Or even to (as in js):
\foo bar -> { foo, bar }
-
About Unit:
PureScript Haskell Type Unit
Type ()
Value unit
Value ()
-
About IO:
If you're looking what would be equivalent to
IO ()
in Haskell or just wondering what the heck isEff (foo :: FOO) Unit
:PureScript have improved implementation of
IO
monad in Haskell, the main difference is thatEff
monad (in PureScript) have additional parameter to specify limitation of possible side-effects (such asCONSOLE
,DOM
,REF
, etc.) so you can have more precise control ofIO
stuff.You defenitely should read official docs about this, the story couldn't be told in few sentences.
Few tips about Eff (Eff means effects):
IO ()
is kindaforall eff. Eff eff Unit
;- You must type your own
Eff
monads providing type of side-effects which it going to make (e.g.Eff (console :: CONSOLE) Unit
); - But usually it's better to allow to use your monad inside more complex ones
by making it polymorphic (e.g.
forall eff. Eff (console :: CONSOLE | eff)
, that means it can doCONSOLE
stuff but not limited to be used in context of others); |
could be read asas
(this aliases whole block inside parentheses).
For async stuff (kinda threading, but remember you're in js world, it's not really threads) you have similar
Aff
monad. You also should read docs about this too.Few tips about Aff:
- Doing
Aff
is kinda doingforkIO
in Haskell I believe; - Use
launchAff
orlaunchAff_
to runAff
fromEff
monad asynchronously; - Use
forkAff
to run anotherAff
fromAff
monad asynchronously; - Use
liftEff
(Control.Monad.Eff.Class
frompurescript-eff
) to executeEff
fromAff
monad; - Use
liftEff'
(Control.Monad.Aff
frompurescript-aff
) to executeEff
fromAff
monad ifEff
monad hasEXCEPTION
effect.
Keep in mind that PureScript is strict by default, so using:
if condition then someMonad foo bar else pure unit
could be better than:
when condition $ someMonad foo bar
in sense of efficiency, because
if
condition compiles to native jsif
condition whilewhen
constructs function reference with possible partial application.See also:
-
About booleans
PureScript Haskell Type Boolean
Type Bool
Value true
Value True
Value false
Value False
-
About tuples
In PureScript there's no special syntax for tuples.
You also need to install
purescript-tuples
.PureScript Haskell Type Tuple Bool Int
Type (Bool, Int)
Value Tuple true 42
Value (True, 42)
Pattern (Tuple x y)
Pattern (x, y)
-
About lists
PureScript has builtin
Array
s. FunctionalList
s are provided bypurescript-lists
package.[1,2,3]
will produce anArray Int
(not[Int]
because in PureScript there's no sugar for typingArray
s/List
s).PureScript doesn't have special syntax for
Array
comprehensions.
Here is an example of doing comprehension using monads:factors :: Int -> Array (Tuple Int Int) factors n = do a <- 1 .. n b <- 1 .. a guard $ a * b == n pure $ Tuple a b
An example of
Array
patterns:f [] = -1 f [x] = x f [x, y] = x * y f _ = 0
There's no builtin cons for
Array
s for pattern-matching (some performance issues) but some helpers are provided bypurescript-arrays
package.See also about this:
https://stackoverflow.com/questions/42450347/purescript-pattern-match-arrays-of-unknown-length#42450443Pattern-matching on
List
s:PureScript Haskell (Cons x xs)
(x : xs)
-
About records:
Records in PureScript isn't limited to be used in context of
data
, they're independent, you don't need (but may) have a wrapper for a record.Here is an example of a function that works with records:
foo :: { foo :: String, bar :: Int } -> Int foo x = x.bar
Type of
foo
is equivalent to:foo :: Record (foo :: String, bar :: Int) -> Int
foo
also can deal with any record that havebar :: Int
if it's typed like this:foo :: forall r. { bar :: Int | r } -> Int
You can read about
|
above, it acts here the same way.Constructing new records is simple:
bar = { foo: "Foo", bar: 42, baz: true }
But keep in mind that when you construct new record you use
:
but when you update a record you use=
:bar { bar = 34 }
An example how to update a nested record:
foo { bar { baz { bzz = 42 } } }
Destructuring also works as in js:
-
foo x = log x.bar
foo { bar } = <- log bar
foo { bar: baz } = <- log baz
-
foo = do x <- bar log x.baz
foo = do { baz } <- bar log baz
foo = do { baz: bzz } <- bar log bzz
-
-
About deriving type class instance:
Deriving instances separated from
data
, here's an example:derive instance eqLocation :: Eq Location derive instance genericLocation :: Generic Location instance showLocation :: Show Location where show = gShow
Names
eqLocation
,genericLocation
andshowLocation
is just for produced js code, they're named like this just by convention but they can be named differently. -
About imports:
In PureScript you don't have
qualified
keyword for imports, if an import haveas
alias it isqualified
.In PureScript
as
keyword must be places after everything (even after explicit imports).PureScript Haskell import Data.Foo as Foo
import qualified Data.Foo as Foo
import Data.Foo as Foo (foo)
import qualified Data.Foo (foo) as Foo
-
About some packages:
Maybe
isn't included, installpurescript-maybe
purescript-console
for writing to the consolepurescript-nullable
to deal with jsnull
s (when you really need it)purescript-generics
to deal withGeneric
stuffpurescript-lens
if you're looking for Kmett's lenses
This is pretty short list that supposed to get basic stuff as fast as possible, read articles by this links to go deeper: