strict by default
- https://pursuit.purescript.org
- http://try.purescript.org (online REPL)
- https://leanpub.com/purescript/read
- https://github.com/unclechu/purescript-for-haskellers/blob/master/README.md
- https://github.com/purescript/documentation/blob/master/language/Differences-from-Haskell.md
- https://mmhaskell.com/blog/2018/10/15/getting-started-with-purescript
Prelude:
-
like
{-# LANGUAGE NoImplicitPrelude #-} -
Preludealso isn't distributed with PureScript compiler -
Explicit dependency
purescript-preludeand import it:import Prelude
forall:
- like
{-# LANGUAGE ExplicitForAll #-}
- unicode:
-
unicode is allowed by default
-
basic unicode symbols included
∷,←,→,⇐,⇒,∀ -
we used
infixr 9 compose as ∘ infixr 9 composeFlipped as ⋙ -- gave up at some point infixr 9 composeFlipped as ⟡ -- in favour for this one (nicer symetry) compose2 :: forall a b c d g. Semigroupoid g => g b a -> (d -> g c b) -> d -> g c a compose2 f g = (f ∘ _) ∘ g infixr 9 compose2 as ∘∘ infixl 9 applyFlipped as |>
-
also
(^|>) (^|>^) (|>^) ($^) (^$) (^$^) (^#) (#^) (^#^) (^⟡) (⟡^) (⟡^⟡) (^⟡^⟡) (⟡^⟡^) (^⟡^⟡^) (^∘) (∘^) (∘^∘) (^∘^∘) (∘^∘^) (^∘^∘^) (^<$>) (<#>^)
-
Basic operators:
Haskell PureScript Restaum (.)(<<<)(∘)flip (.)(>>>)(⟡)(&)(#)(<&>)(<#>)a >> ba *> bb << ab <* a
-
Haskell:
(&),(<&>)- fromData.Function
-
PureScript:
(<>)-Semigrouptype class - always instead of haskelish(++)(*>),(<*)-Applytype class (granurality)
-
Haskell
f $ x = f x
-
PureScript
apply f x = f x infixr 0 apply as $
-
Basic functions / data types:
Haskell PureScript ()Unitfor type,unitfor valuefmapmap(Functortype class)errorunsafeThrow
fromControl.Monad.Eff.Exception.Unsafe
frompurescript-exceptionsforkIOforkAff
fromControl.Monad.Aff
frompurescript-affControl.Concurrent.MVarControl.Monad.Aff.AVarfrompurescript-affundefined? mapMtraverseforMfor_foo?foo_n/a [1 .. 3]1..3
- btw
3..1is[3,2,1](empty in Haskell)
-
point-free (and sections):
Obligatory ghost place for a value in PS:
Haskell PureScript (+ 2)(_ + 2)(2 +)(2 + _)PureScript:
case _ of Just x -> 34 Nothing -> 42
Haskell (with
{-# LANGUAGE LambdaCase #-}):\case Just x -> 34 Nothing -> 42
Update a record - include ghost place marker:
_ { foo = 42 }Nested updates:
_ { foo { bar { baz = 42 } } }Fill record:
{ foo: _, bar: _ }equivalent to:
\foo bar -> { foo: foo, bar: bar }which can be simplified to:
\foo bar -> { foo, bar } -
About IO:
-
Effect
Haskell PureScript IO ()Effect Unit -
Eff (legacy)
Haskell PureScript legacy (Member Console effs) => Eff effs a`forall eff. Eff (console :: CONSOLE -
Haskell -
freer. -
Aff
launchAff_ :: forall a. Aff a -> Effect UnitforkAff :: forall a. Aff a -> Aff (Fiber a)
-
Booleans
PureScript Haskell Type BooleanType BoolValue trueValue TrueValue falseValue False -
Tuples
Package
purescript-tuplesPureScript Haskell Type Tuple Bool IntType (Bool, Int)Value Tuple true 42Value (True, 42)Pattern (Tuple x y)Pattern (x, y) -
Lists
-
PureScript has built-in
Arrays -
functional
Lists are provided bypurescript-listspackage- same with
purescript-maybe
- same with
-
[1,2,3]- a value ofArray Int -
no sugar
[Int]for array types -
no sugar for list (array) comprehensions (
donotation used)Arraypatternsf [] = -1 f [x] = x f [x, y] = x * y f _ = 0
ListpatternsPureScript Haskell (Cons x xs)(x : xs)
-
Records:
foo :: { foo :: String, bar :: Int } -> Int foo x = x.bar
Type of
foois equivalent to:foo :: Record (foo :: String, bar :: Int) -> Int
any record that have
bar :: Intfoo :: forall r. { bar :: Int | r } -> Int
bar = { foo: "Foo", bar: 42, baz: true }for update - use
=instead of:bar { bar = 34 }foo { bar { baz { bzz = 42 } } }destructuring
foo x = log x.bar
foo { bar } = log barfoo { bar: baz } = log baz -
Deriving type class instance:
- separate lines
derive instance eqLocation :: Eq Location derive instance genericLocation :: Generic Location instance showLocation :: Show Location where show = gShow
- names required
- no orphans btw
-
Imports:
no
qualifiedkeywordaskeyword at the endHaskell PureScript import qualified Data.Foo as Fooimport Data.Foo as Fooimport qualified Data.Foo as Foo (foo)import Data.Foo (foo) as Foo -
Type classes
-
arrow direction
class (Eq a) <= Ord a where ...
-
granurality
Categoryhas a superclassSemigroupoidwhich provides(<<<), and does not require an identity.Applicativehas a superclassApply, which provides(<*>)and does not require an implementation forpure.
- Newtypes
import Data.Newtype (class Newtype)
newtype Route =
Route
{ name :: String
, queryParams :: QueryParams
}
derive instance newtypeRoute :: Newtype Route _- PureScript:
wrap/unwrap, Haskell:pack/unpack
r = Route { "Ala", params }
v1 = case r of
Route rr -> rr.name
v2 = (unwrap r).name
v3 = r # unwrap # _.name
v4 = _.name $ unwrap $ r
v5 = r ^|> _.name
v6 = r ^# _.name
v7 = r.name -- in zyla PureScript forkdata Bool = True | False
newtype Year = Year Int
newtype Person = Person { name: String, surname: String }
type PersonRecord = { name: String, surname: String }
newtype Person2 = Person2 PersonRecord---------- apply ----------
x |> f
x # f
f x
x ^|> f
unwrap x # f
x ^|>^ f
unwrap x # f # unwrap
---------- fmaps ----------
f ^<$> x
unwrap ⟡ f <$> x
x <#>^ f
x <#> unwrap ⟡ f
---------- compose ----------
f ^∘ g
unwrap ∘ f ∘ g
f ∘^ g
f ∘ g ∘ unwrap
f ∘^∘ g
f ∘ unwrap ∘ g
f ^∘^∘ g
unwrap ∘ f ∘ unwrap ∘ g
f ∘^∘^ g
f ∘ unwrap ∘ g ∘ unwrap
f ^∘^∘^ g
unwrap ∘ f ∘ unwrap ∘ g ∘ unwrap
---------- compose flipped ----------
f ^⟡ g
unwrap ⟡ f ⟡ g
f ⟡^ g
f ⟡ g ⟡ unwrap
f ⟡^⟡ g
f ⟡ unwrap ⟡ g
f ^⟡^⟡ g
unwrap ⟡ f ⟡ unwrap ⟡ g
f ⟡^⟡^ g
f ⟡ unwrap ⟡ g ⟡ unwrap
f ^⟡^⟡^ g
unwrap ⟡ f ⟡ unwrap ⟡ g ⟡ unwrap- Full code example
Haskell
sourceList :: [Int]
sourceList = [1..100]
allTriples :: [(Int, Int, Int)]
allTriples =
[ (a, b, c)
| a <- sourceList
, b <- sourceList
, c <- sourceList
]
isPythagorean :: (Int, Int, Int) -> Bool
isPythagorean (a, b, c) = a ^ 2 + b ^ 2 == c ^ 2
isSmallEnough :: (Int, Int, Int) -> Bool
isSmallEnough (a, b, c) = a + b + c < 100
finalAnswer :: [(Int, Int, Int)]
finalAnswer = filter
(\t -> isPythagorean t && isSmallEnough t)
allTriplesPureScript
import Data.List (List, range, filter)
import Data.Int (pow)
import Prelude
sourceList :: List Int
sourceList = range 1 100
newtype Triple = Triple
{ a :: Int
, b :: Int
, c :: Int
}
allTriples :: List Triple
allTriples = do
a <- sourceList
b <- sourceList
c <- sourceList
pure $ Triple { a, b, c }
isPythagorean :: Triple -> Boolean
isPythagorean (Triple triple) = pow triple.a 2 + pow triple.b 2 == pow triple.c 2
isSmallEnough :: Triple -> Boolean
isSmallEnough (Triple triple) = triple.a + triple.b + triple.c < 100
finalAnswer :: List Triple
finalAnswer = filter
(\triple -> isPythagorean triple && isSmallEnough triple)
allTriples- My former project
-
~80 KLOC PS, ~80 KLOC Haskell
-
anonymized examples of
unwraphell (fun?)let resultDataD = map (_.report ∘ unwrap) ∘ join ∘ map hush ∘ fromLoaded <$> requestResultD flip F.singleOption choiceId <$> liftMaybe (Map.lookup itemId (unwrap (unwrap env.menu).items) <#> unwrap >>> _.name >>> getLocalization locale merge (toUnfoldable (Map.keys (unwrap (unwrap items).items))) form.itemsList tableStyled (\x -> mwhen (Just (unwrap x).itemId == ((_.itemId ∘ unwrap) <$> env.highlightedItem)) ("class" := "success"))
-
In Haskell indentation, in PureScript not (kind of accidentally)
-
PureScript more responsive IDE tooling (when works)
-
In PS a lot of FRP code (
Specularby zyla) -
lots of in house machinery
- more layers on top of
Specular- widgets
- forms
- validations
- rpc handling layers (json-rpc)
- interface
- error handling
- generate-ps for data types (domain and rpc requests/responses)
- including robust tests of correctness of generated files
- more layers on top of
-
evolving conventions
-
performance issues (
Specularengine rewritten into inremental style)- tracing