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, andPreludealso isn't distributed with PureScript compiler.You need to install dependency
purescript-preludeand to import it:import Prelude
-
About
forall:PureScript acts like
{-# LANGUAGE ExplicitForAll #-}in Haskell.You need to explicitly declare
forallfor 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(.>)fromflowpackage(#)(Data.Function.&)frombasepackage(<#>)(Data.Functor.<&>)frombasepackage(<>)(Semigrouptype class)(++)a *> b(Applytype class)a >> bb <* a(Applytype class)b << a -
Basic functions equivalents (those which differ):
PureScript Haskell map(Functortype class)fmap(works likemapfor lists)unsafeThrow
fromControl.Monad.Eff.Exception.Unsafe
frompurescript-exceptionspackageerrorforkAff
fromControl.Monad.Aff
frompurescript-affpackageforkIOIf you're looking for Haskell's
Control.Concurrent.MVarlook at PureScript'sControl.Monad.Aff.AVarfrompurescript-affpackage. -
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 UnitType ()Value unitValue () -
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
IOmonad in Haskell, the main difference is thatEffmonad (in PureScript) have additional parameter to specify limitation of possible side-effects (such asCONSOLE,DOM,REF, etc.) so you can have more precise control ofIOstuff.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
Effmonads 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 doCONSOLEstuff 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
Affmonad. You also should read docs about this too.Few tips about Aff:
- Doing
Affis kinda doingforkIOin Haskell I believe; - Use
launchAfforlaunchAff_to runAfffromEffmonad asynchronously; - Use
forkAffto run anotherAfffromAffmonad asynchronously; - Use
liftEff(Control.Monad.Eff.Classfrompurescript-eff) to executeEfffromAffmonad; - Use
liftEff'(Control.Monad.Afffrompurescript-aff) to executeEfffromAffmonad ifEffmonad hasEXCEPTIONeffect.
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
ifcondition compiles to native jsifcondition whilewhenconstructs function reference with possible partial application.See also:
-
About booleans
PureScript Haskell Type BooleanType BoolValue trueValue TrueValue falseValue False -
About tuples
In PureScript there's no special syntax for tuples.
You also need to install
purescript-tuples.PureScript Haskell Type Tuple Bool IntType (Bool, Int)Value Tuple true 42Value (True, 42)Pattern (Tuple x y)Pattern (x, y) -
About lists
PureScript has builtin
Arrays. FunctionalLists are provided bypurescript-listspackage.[1,2,3]will produce anArray Int(not[Int]because in PureScript there's no sugar for typingArrays/Lists).PureScript doesn't have special syntax for
Arraycomprehensions.
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
Arraypatterns:f [] = -1 f [x] = x f [x, y] = x * y f _ = 0
There's no builtin cons for
Arrays for pattern-matching (some performance issues) but some helpers are provided bypurescript-arrayspackage.See also about this:
https://stackoverflow.com/questions/42450347/purescript-pattern-match-arrays-of-unknown-length#42450443Pattern-matching on
Lists: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
foois equivalent to:foo :: Record (foo :: String, bar :: Int) -> Int
fooalso can deal with any record that havebar :: Intif 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 barfoo { 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,genericLocationandshowLocationis 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
qualifiedkeyword for imports, if an import haveasalias it isqualified.In PureScript
askeyword must be places after everything (even after explicit imports).PureScript Haskell import Data.Foo as Fooimport qualified Data.Foo as Fooimport Data.Foo as Foo (foo)import qualified Data.Foo (foo) as Foo -
About some packages:
Maybeisn't included, installpurescript-maybepurescript-consolefor writing to the consolepurescript-nullableto deal with jsnulls (when you really need it)purescript-genericsto deal withGenericstuffpurescript-lensif 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: