├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PureScript For Haskellers 2 | 3 | Some info that supposed to help 4 | to understand [PureScript](http://www.purescript.org/) 5 | from [Haskell](https://www.haskell.org/) perspective. 6 | 7 | If you already know js it will be even simplier. 8 | 9 | ## About PureScript 10 | 11 | PureScript written in Haskell but usually distributed as binaries via NPM. 12 | 13 | It uses **Bower** instead of **NPM** because **Bower** 14 | have flat dependencies and better dependency resolution.
15 | This explained better here: http://harry.garrood.me/blog/purescript-why-bower/ 16 | 17 | PureScript is **strict** by default! 18 | 19 | ### Useful links 20 | 21 | - https://pursuit.purescript.org 22 | (kinda like https://stackage.org for Haskell) 23 | - http://try.purescript.org (online REPL) 24 | 25 | ### Chatting 26 | 27 | - #purescript on Freenode
28 | [Matrix](https://matrix.org/) 29 | bridge: https://riot.im/app/#/room/#freenode_#purescript:matrix.org 30 | - https://gitter.im/purescript/purescript
31 | [Matrix](https://matrix.org/) 32 | bridge: https://riot.im/app/#/room/#gitter_purescript=2Fpurescript:matrix.org 33 | 34 | ## From Haskell perspective 35 | 36 | 1. **About `Prelude`**: 37 | 38 | PureScript acts like `{-# LANGUAGE NoImplicitPrelude #-}` in Haskell, 39 | and `Prelude` also isn't distributed with PureScript compiler. 40 | 41 | You need to install dependency `purescript-prelude` and to import it: 42 | 43 | ```purescript 44 | import Prelude 45 | ``` 46 | 47 | 2. **About `forall`**: 48 | 49 | PureScript acts like `{-# LANGUAGE ExplicitForAll #-}` in Haskell. 50 | 51 | You need to explicitly declare `forall` for every polymorphic type variable. 52 | 53 | 3. **About unicode**: 54 | 55 | In PureScript using unicode is allowed by default. 56 | 57 | Basic unicode symbols also included 58 | (such as `∷`, `←`, `→`, `⇐`, `⇒`, `∀`, etc.) 59 | 60 | 4. **Basic operators equivalents** (those which differ): 61 | 62 | | PureScript | Haskell | 63 | | --- | --- | 64 | | `(<<<)` | `(.)` | 65 | | `(>>>)` | `flip (.)` or `(.>)` from `flow` package | 66 | | `(#)` | `(Data.Function.&)` from `base` package | 67 | | `(<#>)` | `(Data.Functor.<&>)` from `base` package | 68 | | `(<>)` (`Semigroup` type class) | `(++)` | 69 | | `a *> b` (`Apply` type class) | `a >> b` | 70 | | `b <* a` (`Apply` type class) | `b << a` | 71 | 72 | 5. **Basic functions equivalents** (those which differ): 73 | 74 | | PureScript | Haskell | 75 | | --- | --- | 76 | | `map` (`Functor` type class) | `fmap` (works like `map` for lists) | 77 | | `unsafeThrow`
from `Control.Monad.Eff.Exception.Unsafe`
from `purescript-exceptions` package | `error` | 78 | | `forkAff`
from `Control.Monad.Aff`
from `purescript-aff` package | `forkIO` | 79 | 80 | If you're looking for **Haskell**'s `Control.Concurrent.MVar` look at 81 | **PureScript**'s `Control.Monad.Aff.AVar` from `purescript-aff` package. 82 | 83 | 6. **About point-free style**: 84 | 85 | For partially applied operators you must specify *ghost* place for a value: 86 | 87 | | PureScript | Haskell | 88 | | --- | --- | 89 | | `(_ + 2)` | `(+ 2)` | 90 | | `(2 + _)` | `(2 +)` | 91 | 92 | You're defenitely familiar with `{-# LANGUAGE LambdaCase #-}` in **Haskell**: 93 | 94 | In **PureScript** you have kinda the same, but again, 95 | you need to explicitly set *ghost* place for a value: 96 | 97 | ```purescript 98 | case _ of 99 | Just x -> 34 100 | Nothing -> 42 101 | ``` 102 | 103 | That in **Haskell** would be: 104 | 105 | ```haskell 106 | \case 107 | Just x -> 34 108 | Nothing -> 42 109 | ``` 110 | 111 | To update a record in **PureScript** you also use a *ghost* place marker: 112 | 113 | ```purescript 114 | _ { foo = 42 } 115 | ``` 116 | 117 | But in **PureScript** you also able to easily modify nested records without 118 | even using stuff like lenses: 119 | 120 | ```purescript 121 | _ { foo { bar { baz = 42 } } } 122 | ``` 123 | 124 | You able create a function that fills record values this way: 125 | 126 | ```purescript 127 | { foo: _, bar: _ } 128 | ``` 129 | 130 | Which is equivalent to: 131 | 132 | ```purescript 133 | \foo bar -> { foo: foo, bar: bar } 134 | ``` 135 | 136 | Or even to (as in js): 137 | 138 | ```purescript 139 | \foo bar -> { foo, bar } 140 | ``` 141 | 142 | 7. **About Unit**: 143 | 144 | | PureScript | Haskell | 145 | | --- | --- | 146 | | Type `Unit` | Type `()` | 147 | | Value `unit` | Value `()` | 148 | 149 | 8. **About IO**: 150 | 151 | If you're looking what would be equivalent to `IO ()` in **Haskell** or just 152 | wondering what the heck is `Eff (foo :: FOO) Unit`: 153 | 154 | **PureScript** have improved implementation of `IO` monad in **Haskell**, the 155 | main difference is that `Eff` monad (in **PureScript**) have additional 156 | parameter to specify limitation of possible side-effects (such as `CONSOLE`, 157 | `DOM`, `REF`, etc.) so you can have more precise control of `IO` stuff. 158 | 159 | You defenitely should read official docs about this, the story couldn't be 160 | told in few sentences. 161 | 162 | Few tips about **Eff** (*Eff* means *effects*): 163 | 164 | - `IO ()` is kinda `forall eff. Eff eff Unit`; 165 | - You must type your own `Eff` monads providing type of side-effects which it 166 | going to make (e.g. `Eff (console :: CONSOLE) Unit`); 167 | - But usually it's better to allow to use your monad inside more complex ones 168 | by making it polymorphic (e.g. 169 | `forall eff. Eff (console :: CONSOLE | eff)`, that means it can do 170 | `CONSOLE` stuff but not limited to be used in context of others); 171 | - `|` could be read as `as` (this aliases whole block inside parentheses). 172 | 173 | For async stuff (kinda threading, but remember you're in js world, it's not 174 | really threads) you have similar `Aff` monad. You also should read docs about 175 | this too. 176 | 177 | Few tips about **Aff**: 178 | 179 | - Doing `Aff` is kinda doing `forkIO` in **Haskell** I believe; 180 | - Use `launchAff` or `launchAff_` to run `Aff` from `Eff` monad 181 | asynchronously; 182 | - Use `forkAff` to run another `Aff` from `Aff` monad asynchronously; 183 | - Use `liftEff` (`Control.Monad.Eff.Class` from `purescript-eff`) 184 | to execute `Eff` from `Aff` monad; 185 | - Use `liftEff'` (`Control.Monad.Aff` from `purescript-aff`) 186 | to execute `Eff` from `Aff` monad if `Eff` monad has `EXCEPTION` effect. 187 | 188 | Keep in mind that **PureScript** is strict by default, so using: 189 | 190 | ```purescript 191 | if condition 192 | then someMonad foo bar 193 | else pure unit 194 | ``` 195 | 196 | could be better than: 197 | 198 | ```purescript 199 | when condition $ someMonad foo bar 200 | ``` 201 | 202 | in sense of efficiency, because `if` condition compiles to native js `if` 203 | condition while `when` constructs function reference with possible partial 204 | application. 205 | 206 | See also: 207 | - https://pursuit.purescript.org/packages/purescript-eff 208 | - https://pursuit.purescript.org/packages/purescript-aff 209 | 210 | 9. **About booleans** 211 | 212 | | PureScript | Haskell | 213 | | --- | --- | 214 | | Type `Boolean` | Type `Bool` | 215 | | Value `true` | Value `True` | 216 | | Value `false` | Value `False` | 217 | 218 | 10. **About tuples** 219 | 220 | In **PureScript** there's no special syntax for tuples. 221 | 222 | You also need to install `purescript-tuples`. 223 | 224 | | PureScript | Haskell | 225 | | --- | --- | 226 | | Type `Tuple Bool Int` | Type `(Bool, Int)` | 227 | | Value `Tuple true 42` | Value `(True, 42)` | 228 | | Pattern `(Tuple x y)` | Pattern `(x, y)` | 229 | 230 | 11. **About lists** 231 | 232 | **PureScript** has builtin `Array`s. 233 | Functional `List`s are provided by `purescript-lists` package. 234 | 235 | `[1,2,3]` will produce an `Array Int` 236 | (not `[Int]` because in **PureScript** 237 | there's no sugar for typing `Array`s/`List`s). 238 | 239 | **PureScript** doesn't have special syntax for `Array` comprehensions.
240 | Here is an example of doing comprehension using monads: 241 | 242 | ```purescript 243 | factors :: Int -> Array (Tuple Int Int) 244 | factors n = do 245 | a <- 1 .. n 246 | b <- 1 .. a 247 | guard $ a * b == n 248 | pure $ Tuple a b 249 | ``` 250 | 251 | An example of `Array` patterns: 252 | 253 | ```purescript 254 | f [] = -1 255 | f [x] = x 256 | f [x, y] = x * y 257 | f _ = 0 258 | ``` 259 | 260 | There's no builtin *cons* for `Array`s for pattern-matching 261 | (some performance issues) 262 | but some helpers are provided by `purescript-arrays` package. 263 | 264 | See also about this:
265 | https://stackoverflow.com/questions/42450347/purescript-pattern-match-arrays-of-unknown-length#42450443 266 | 267 | Pattern-matching on `List`s: 268 | 269 | | PureScript | Haskell | 270 | | --- | --- | 271 | | `(Cons x xs)` | `(x : xs)` | 272 | 273 | 12. **About records**: 274 | 275 | Records in **PureScript** isn't limited to be used in context of `data`, 276 | they're independent, you don't need (but may) have a wrapper for a record. 277 | 278 | Here is an example of a function that works with records: 279 | 280 | ```purescript 281 | foo :: { foo :: String, bar :: Int } -> Int 282 | foo x = x.bar 283 | ``` 284 | 285 | Type of `foo` is equivalent to: 286 | 287 | ```purescript 288 | foo :: Record (foo :: String, bar :: Int) -> Int 289 | ``` 290 | 291 | `foo` also can deal with any record that have `bar :: Int` 292 | if it's typed like this: 293 | 294 | ```purescript 295 | foo :: forall r. { bar :: Int | r } -> Int 296 | ``` 297 | 298 | You can read about `|` above, it acts here the same way. 299 | 300 | Constructing new records is simple: 301 | 302 | ```purescript 303 | bar = { foo: "Foo", bar: 42, baz: true } 304 | ``` 305 | 306 | But keep in mind that when you construct new record you use `:` but when you 307 | update a record you use `=`: 308 | 309 | ```purescript 310 | bar { bar = 34 } 311 | ``` 312 | 313 | An example how to update a nested record: 314 | 315 | ```purescript 316 | foo { bar { baz { bzz = 42 } } } 317 | ``` 318 | 319 | Destructuring also works as in js: 320 | 321 | 1. 322 | ```purescript 323 | foo x = log x.bar 324 | ``` 325 | ```purescript 326 | foo { bar } = <- log bar 327 | ``` 328 | ```purescript 329 | foo { bar: baz } = <- log baz 330 | ``` 331 | 2. 332 | ```purescript 333 | foo = do 334 | x <- bar 335 | log x.baz 336 | ``` 337 | ```purescript 338 | foo = do 339 | { baz } <- bar 340 | log baz 341 | ``` 342 | ```purescript 343 | foo = do 344 | { baz: bzz } <- bar 345 | log bzz 346 | ``` 347 | 348 | 13. **About deriving type class instance**: 349 | 350 | Deriving instances separated from `data`, here's an example: 351 | 352 | ```purescript 353 | derive instance eqLocation :: Eq Location 354 | derive instance genericLocation :: Generic Location 355 | instance showLocation :: Show Location where show = gShow 356 | ``` 357 | 358 | Names `eqLocation`, `genericLocation` and `showLocation` is just for 359 | produced js code, they're named like this just by convention but they can be 360 | named differently. 361 | 362 | 14. **About imports**: 363 | 364 | In **PureScript** you don't have `qualified` keyword for imports, 365 | if an import have `as` alias it **is** `qualified`. 366 | 367 | In **PureScript** `as` keyword must be places after everything 368 | (even after explicit imports). 369 | 370 | | PureScript | Haskell | 371 | | --- | --- | 372 | | `import Data.Foo as Foo` | `import qualified Data.Foo as Foo` | 373 | | `import Data.Foo as Foo (foo)` | `import qualified Data.Foo (foo) as Foo` | 374 | 375 | 15. **About some packages**: 376 | 377 | - `Maybe` isn't included, install `purescript-maybe` 378 | - `purescript-console` for writing to the console 379 | - `purescript-nullable` to deal with js `null`s (when you really need it) 380 | - `purescript-generics` to deal with `Generic` stuff 381 | - `purescript-lens` if you're looking for Kmett's lenses 382 | 383 | This is pretty short list that supposed to get basic stuff as fast as possible, 384 | read articles by this links to go deeper: 385 | 386 | - https://github.com/purescript/documentation/blob/master/language/Differences-from-Haskell.md 387 | - https://github.com/purescript/documentation/blob/master/guides/Getting-Started.md 388 | - https://github.com/purescript/documentation/tree/master/language 389 | --------------------------------------------------------------------------------