{"id":15059414,"url":"https://github.com/laurentpayot/purescript-for-elm-developers","last_synced_at":"2025-04-10T05:30:47.405Z","repository":{"id":207361848,"uuid":"700757066","full_name":"laurentpayot/purescript-for-elm-developers","owner":"laurentpayot","description":"PureScript crash course targeted at Elm developers","archived":false,"fork":false,"pushed_at":"2024-09-17T09:09:39.000Z","size":483,"stargazers_count":36,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-03-24T06:51:31.779Z","etag":null,"topics":["cheatsheet","crash-course","elm","elm-architecture","elm-lang","learning","learning-resources","purescript","purescript-lang","purescript-language"],"latest_commit_sha":null,"homepage":"","language":"PureScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/laurentpayot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-05T08:30:57.000Z","updated_at":"2024-09-22T17:50:15.000Z","dependencies_parsed_at":"2024-09-29T05:20:32.232Z","dependency_job_id":"0e945f53-e3f0-41aa-82b3-4a7ef08e3d38","html_url":"https://github.com/laurentpayot/purescript-for-elm-developers","commit_stats":null,"previous_names":["laurentpayot/purescript-for-elm-developers"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laurentpayot%2Fpurescript-for-elm-developers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laurentpayot%2Fpurescript-for-elm-developers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laurentpayot%2Fpurescript-for-elm-developers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/laurentpayot%2Fpurescript-for-elm-developers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/laurentpayot","download_url":"https://codeload.github.com/laurentpayot/purescript-for-elm-developers/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248162955,"owners_count":21057842,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cheatsheet","crash-course","elm","elm-architecture","elm-lang","learning","learning-resources","purescript","purescript-lang","purescript-language"],"created_at":"2024-09-24T22:43:16.119Z","updated_at":"2025-04-10T05:30:47.363Z","avatar_url":"https://github.com/laurentpayot.png","language":"PureScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# PureScript for Elm developers 🤯\n\nThis README file is a crash course on [PureScript](https://www.purescript.org/) targeted at [Elm](https://elm-lang.org/) developers. It is\nbased on information picked from:\n\n- https://github.com/alpacaaa/elm-to-purescript-cheatsheet\n- https://github.com/purescript/documentation\n- https://book.purescript.org\n- https://jordanmartinez.github.io/purescript-jordans-reference-site/\n- https://learnxinyminutes.com/docs/purescript/\n- https://gist.github.com/justinwoo/0118be3e4a0d7394a99debbde2515f9b\n- https://www.tobyhobson.com/posts/cats/\n- https://github.com/alpacaaa/zero-bs-haskell\n\nSometimes I did a shameless copy-paste instead of writing a bad paraphrase. I think it is fair use but please let me know if I am infringing any copyright. Feel free to open an issue if you find a mistake.\n\nHappy monad lifting! 🏋\n\nLaurent\n\n\u003chr\u003e\n\n## Developer Experience\n\n### VS Code Plugins\n\n- [PureScript Language Support](https://marketplace.visualstudio.com/items?itemName=nwolverson.language-purescript): Syntax highlighting for the PureScript programming language\n- [PureScript IDE](https://marketplace.visualstudio.com/items?itemName=nwolverson.ide-purescript): PureScript IntelliSense, tooltip, errors, code actions with language-server-purescript/purs IDE server\n- [Purty](https://marketplace.visualstudio.com/items?itemName=mvakula.vscode-purty): PureScript formatter\n\n\n### Bundling\n\n[Spago](https://github.com/purescript/spago) is the PureScript package manager and build tool.\n\nThe PureScript compiler (transpiler) is quite fast. The compiler messages are not as friendly as with Elm, unfortunately.\n\n## Common packages\n\n[Pursuit](https://pursuit.purescript.org/) is the home of PureScript packages documentation. It lets you search by package, module, and function names, as well as approximate type signatures.\n\n| **Elm**                                  | **Purescript**                           | **Notes**                                |\n| ---------------------------------------- | ---------------------------------------- | ---------------------------------------- |\n| [String](http://package.elm-lang.org/packages/elm-lang/core/latest/String) | [Data.String](https://pursuit.purescript.org/packages/purescript-strings/docs/Data.String) |                                          |\n| [Maybe](http://package.elm-lang.org/packages/elm-lang/core/latest/Maybe) | [Data.Maybe](https://pursuit.purescript.org/packages/purescript-maybe/docs/Data.Maybe) |                                          |\n| [Result](http://package.elm-lang.org/packages/elm-lang/core/latest/Result) | [Data.Either](https://pursuit.purescript.org/packages/purescript-either/docs/Data.Either) | `Err` is `Left` and `Ok` is `Right`      |\n| [Array](http://package.elm-lang.org/packages/elm-lang/core/latest/Array) | [Data.Array](https://pursuit.purescript.org/packages/purescript-arrays/docs/Data.Array) | `[]` is the empty array |\n| [List](http://package.elm-lang.org/packages/elm-lang/core/latest/List) | [Data.List](https://pursuit.purescript.org/packages/purescript-lists/docs/Data.List) | `Nil` is the empty list|\n| [Tuple](http://package.elm-lang.org/packages/elm-lang/core/latest/Tuple) | [Data.Tuple](https://pursuit.purescript.org/packages/purescript-tuples/docs/Data.Tuple) |                                    |\n| [Dict](http://package.elm-lang.org/packages/elm-lang/core/latest/Dict) | [Data.Map](https://pursuit.purescript.org/packages/purescript-maps/docs/Data.Map) |                                          |\n| [Set](http://package.elm-lang.org/packages/elm-lang/core/latest/Set) | [Data.Set](https://pursuit.purescript.org/packages/purescript-sets/docs/Data.Set) |                                          |\n| () | [Data.Unit](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Unit) | `()` is the empty [`Row` type](https://github.com/purescript/documentation/blob/master/language/Types.md#rows) in PureScript |\n| [Never](https://package.elm-lang.org/packages/elm/core/latest/Basics#Never) | [Data.Void](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Void) | |\n| [Debug](http://package.elm-lang.org/packages/elm-lang/core/latest/Debug) | [Debug.Trace](https://pursuit.purescript.org/packages/purescript-debug) | `Debug.spy` is the closest thing to `Debug.log` |\n\n## Common Functions\n\n| **Elm**                                  | **Purescript**                           | **Notes** |\n| ---------------------------------------- | ---------------------------------------- | --------- |\n| `()` | [unit](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Unit#v:unit) |\n| [identity](http://package.elm-lang.org/packages/elm-lang/core/latest/Basics#identity) | [identity](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Function#v:identity) |\n| [always](http://package.elm-lang.org/packages/elm-lang/core/latest/Basics#always) | [const](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Function#v:const) |\n| [never](https://package.elm-lang.org/packages/elm/core/latest/Basics#never) | [absurd](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Void#v:absurd) |\n| [toString](http://package.elm-lang.org/packages/elm-lang/core/latest/Basics#toString) | [show](https://pursuit.purescript.org/packages/purescript-prelude) |\n| `\u003e\u003e`                                     | `\u003e\u003e\u003e`                                    |           |\n| `\u003c\u003c`                                     | `\u003c\u003c\u003c`                                    |           |\n| `\\|\u003e`                                    | `#`                                      |           |\n| `\u003c\\|`                                    | `$`                                      |           |\n| `++`                                     | `\u003c\u003e`                                     | [Semigroup](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Semigroup#t:Semigroup) concatenation (`String`, `Array`, `List`, `Tuple`…)|\n\n## Type signatures\n\nType signatures are separated with double colons.\n\n```purs\nsum :: Int -\u003e Int -\u003e Int\n```\n\nPolymorphic functions in PureScript require an explicit `forall` to declare type variables before using them.\n\n```purs\nmap :: forall a b. (a -\u003e b) -\u003e Maybe a -\u003e Maybe b\n```\n\n## Type holes\n\n```purs\nrunApp :: Foo -\u003e Bar Baz String Int Unit -\u003e ?x\n```\n\nIf you’re not sure of a type in a type signature, you can write a type \"hole\" consisting of a question mark followed by a lowercase name. The compiler will generate an error and tell you what type it inferred. Note that to use type holes there must be no other compiler errors.\n\nYou can use type holes everywhere:\n```purs\nfoo :: Int\nfoo = 1 + ?what_could_this_be\n```\n\n## Arrays\n\nIn PureScript, arrays are the most common data structure for sequences of items. They are constructed with square brackets.\n\n```purs\nimport Data.Array ()\n\nmyArray = [2,4,3]\n\n-- Cons (prepend)\nmyNewArray =  1 : [2,4,3] -- [1,2,4,3]\n\nhead [1,2,3,4] -- (Just 1)\ntail [1,2,3,4] -- (Just [2,3,4])\ninit [1,2,3,4] -- (Just [1,2,3])\nlast [1,2,3,4] -- (Just 4)\n\n-- Array access by index starting at 0\n[3,4,5,6,7] !! 2 -- (Just 5)\n\n-- Range\n1..5 -- [1,2,3,4,5]\n\nlength [2,2,2] -- 3\ndrop 3 [1,2,3,4,5] -- [4,5]\ntake 3 [1,2,3,4,5] -- [1,2,3]\nappend [1,2,3] [4,5,6] -- [1,2,3,4,5,6]\n```\n\n### Destructuring\n\nYou can use pattern matching for arrays of a *fixed* length:\n\n```purs\nisEmpty :: forall a. Array a -\u003e Boolean\nisEmpty [] = true\nisEmpty _ = false\n\ntakeFive :: Array Int -\u003e Int\ntakeFive [0, 1, a, b, _] = a * b\ntakeFive _ = 0\n```\n\nFor performance reasons, PureScript does *not* provide a direct way of destructuring arrays of an *unspecified* length. If you need a data structure which supports this sort of matching, the recommended approach is to use lists.\n\nAnother way is to use `uncons` or `unsnoc` to break an array into its first or last element and remaining elements:\n\n```purs\nimport Data.Array (uncons, unsnoc)\n\nuncons [1, 2, 3] -- Just {head: 1, tail: [2, 3]}\nuncons [] -- Nothing\n\nunsnoc [1, 2, 3] -- Just {init: [1, 2], last: 3}\nunsnoc [] -- Nothing\n\ncase uncons myArray of\n  Just { head: x, tail: xs } -\u003e somethingWithXandXs\n  Nothing -\u003e somethingElse\n```\n\nBeware `unsnoc` is O(n) where n is the length of the array.\n\n## Lists\n\n**Be careful!**  The literal `[1,2,3]` has a type of `List Int` in Elm but `Array Int` in Purescript.\n\nPureScript lists are [linked lists](https://en.wikipedia.org/wiki/Linked_list). You can create them using the `Cons` infix alias `:` and `Nil` when there is no link to the next element (end of the list).\n\n```purs\nmyList = 1 : 2 : 3 : Nil\n\nmyNewList = 1 : myList\n```\n\nAnother way to create a list is from a Foldable structure (an Array in this case):\n\n```purs\nmyList = List.fromFoldable [2,4,3]\n```\n\n### Destructuring\n\n```purs\ncase xs of\n  Nil -\u003e ... -- empty list\n  x : rest -\u003e ... -- head and tail\n```\n\n## Foldables\n\n[Data.Foldable](https://pursuit.purescript.org/packages/purescript-foldable-traversable/docs/Data.Foldable) contains common functions (`sum`, `product`, `minimum`, `maximum` etc.) for data structures which can be folded, such as `Array` and `List`.\n\n## Non empty arrays/lists\n\nThere is a [Data.NotEmpty](https://pursuit.purescript.org/packages/purescript-nonempty/7.0.0/docs/Data.NonEmpty) module that defines a generic `NonEmpty` data structure. `:|` is the infix alias for its constructor.\n\nThis quite useful to flatten cases as described in the famous [\"Parse, don’t validate\"](https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-validate/) blog post:\n\n```purs\nimport Data.NonEmpty (NonEmpty, (:|))\n\n-- no Maybe when getting the head\narrayHead :: NonEmpty Array a -\u003e a\narrayHead (x :| _) = x\n```\n\nInstead the generic `Data.NonEmpty` module, use specific modules when possible:\n- [Data.Array.NonEmpty](https://pursuit.purescript.org/packages/purescript-arrays/docs/Data.Array.NonEmpty) to create `NonEmptyArray`\n- [Data.List.NonEmpty](https://pursuit.purescript.org/packages/purescript-lists/docs/Data.List.NonEmpty) to create `NonEmptyList`\n\nFor convenience, [Data.Array.NonEmpty.Internal](https://pursuit.purescript.org/packages/purescript-arrays/7.3.0/docs/Data.Array.NonEmpty.Internal#t:NonEmptyArray) provides the internal constructor  [`NonEmptyArray`](https://pursuit.purescript.org/packages/purescript-arrays/docs/Data.Array.NonEmpty.Internal#t:NonEmptyArray). Beware you can create a `NonEmptyArray` that is actually empty with it so use this at your own risk when you know what you are doing.\n\n## Tuples\n\nTuples are just a data type in Purescript. Use records when possible.\n\n```purs\nimport Data.Tuple (Tuple(..), fst, snd)\n\ncoords2D :: Tuple Int Int\ncoords2D = Tuple 10 20\n\ngetX :: Tuple Int Int -\u003e Int\ngetX coords = fst coords\n\ngetY :: Tuple Int Int -\u003e Int\ngetY coords = snd coords\n```\n\n### Nested tuples\n\nYou can use tuples that are not restricted to two elements with [Data.Tuple.Nested](https://pursuit.purescript.org/packages/purescript-tuples/docs/Data.Tuple.Nested). All nested tuple functions are numbered from 1 to 10:\n\n```purs\nimport Data.Tuple.Nested (Tuple3, tuple3, get2)\n\ncoords3D :: Tuple3 Int Int Int\ncoords3D = tuple3 10 20 30\n\ngetY :: Tuple3 Int Int Int -\u003e Int\ngetY coords = get2 coords\n```\n\n`/\\` is the infix alias for `Tuple` that allows nested tuples of arbitrary length (depth). The same alias exists for types. The previous example could be rewritten as:\n\n```purs\nimport Data.Tuple.Nested (type (/\\), (/\\), get2)\n\ncoords3D :: Int /\\ Int /\\ Int\ncoords3D = 10 /\\ 20 /\\ 30\n\ngetY :: Int /\\ Int /\\ Int -\u003e Int\ngetY coords = get2 coords\n```\n\n### Destructuring\n\n```purs\ndistance2D :: Tuple Int Int -\u003e Int\ndistance2D (Tuple x y) =\n  x * x + y * y\n\ndistance3D :: Int /\\ Int /\\ Int -\u003e Int\ndistance3D (x /\\ y /\\ z) =\n  x * x + y * y + z * z\n```\n\n## Records\n\n```purs\ntype Person =\n  { name :: String\n  , age :: Int\n  }\n\nmyPerson :: Person\nmyPerson = { name: \"Bob\", age: 30 }\n\nedited :: Person\nedited = myPerson { age = 31 }\n\ntoPerson :: String -\u003e Int -\u003e Person\ntoPerson name age =\n  { name: name, age: age }\n\ntoPerson2 :: String -\u003e Int -\u003e Person\ntoPerson2 name age =\n  { name, age }\n\ntoPerson3 :: String -\u003e Int -\u003e Person\ntoPerson3 =\n  { name: _, age: _ } -- equivalent to `\\name age -\u003e { name, age }` (types inferred by the signature)\n```\n\n### Property accessors\n\nIn PureScript `(_ + 5)` is the same as `(\\n -\u003e n + 5)`, so `(_.prop)` is the same as `(\\r -\u003e r.prop)`.\n\n```purs\n_.age myPerson -- 30\n_.address.street myPerson -- \"Main Street\"\n```\n\n### Destructuring\n\n```purs\npersonName :: Person -\u003e String\npersonName { name } = name\n\nbumpAge :: Person -\u003e Person\nbumpAge p@{ age } =\n\tp { age = age + 1 }\n```\n### Pattern matching\n\n```purs\necoTitle {author: \"Umberto Eco\", title: t} = Just t\necoTitle _ = Nothing\n\necoTitle {title: \"Foucault's pendulum\", author: \"Umberto Eco\"} -- (Just \"Foucault's pendulum\")\necoTitle {title: \"The Quantum Thief\", author: \"Hannu Rajaniemi\"} -- Nothing\n-- ecoTitle requires both field to type check\necoTitle {title: \"The Quantum Thief\"} -- Object lacks required property \"author\"\n```\n\n### Row Polymorphism\n\nRow Polymorphism is the equivalent of the extensible records concept in Elm.\n\n```elm\n-- Elm\ngetAge : { a | age : Int } -\u003e Int\ngetAge { age } = age\n```\n\n```purs\n-- PureScript\ngetAge :: forall r. { age :: Int | r } -\u003e Int\ngetAge { age } = age\n```\nIn the above example, the type variable `r` has kind [`Row Type`](https://github.com/purescript/documentation/blob/master/language/Types.md#rows) (an unordered collection of named types, with duplicates).\n\n\n## `where` clause\n\nThe `where` clause is \"syntactic sugar\" for let bindings. Functions defined below the `where` keyword can be used in the function scope and in the `where` scope.\n\n```purs\nfoo :: String -\u003e String -\u003e String\nfoo arg1 arg2 =\n  bar arg1 arg2 \"Welcome to PureScript!\"\n\n  where\n    bar :: String -\u003e String -\u003e String -\u003e String\n    bar s1 s2 s3 =\n      (baz s1) \u003c\u003e (baz s2) \u003c\u003e s3\n\n    baz :: String -\u003e String\n    baz s = \"Hi \" \u003c\u003e s \u003c\u003e \"! \"\n```\n\n## Guards\n\nGuards consist of lines starting with `|` followed by a predicate. They can be used to make function definitions more readable:\n\n```purs\ngreater x y\n  | x \u003e y = true\n  | otherwise = false\n```\n\nExhaustibility of patterns is checked by the compiler. To be considered exhaustive, guards must clearly include a case that is always true. `otherwise` is a synonym for `true` and is commonly used in guards.\n\nGuards may also be used within case expressions, which allow for inline expressions. For example, these are equivalent:\n\n```purs\nfb :: Int -\u003e Effect Unit\nfb = log \u003c\u003c\u003c case _ of\n  n\n    | 0 == mod n 15 -\u003e \"FizzBuzz\"\n    | 0 == mod n 3 -\u003e \"Fizz\"\n    | 0 == mod n 5 -\u003e \"Buzz\"\n    | otherwise -\u003e show n\n```\n\n```purs\nfb :: Int -\u003e Effect Unit\nfb n = log x\n  where\n  x\n    | 0 == mod n 15 = \"FizzBuzz\"\n    | 0 == mod n 3 = \"Fizz\"\n    | 0 == mod n 5 = \"Buzz\"\n    | otherwise = show n\n```\n\n## Data types\n\nInstead of Elm’s `type`, PureScript uses `data`.\nInstead of Elm’s `type alias`, PureScript uses `type`.\n\n```elm\n-- Elm\ntype Direction  = Up | Down\ntype alias Time = Int\n```\n\n```purs\n-- PureScript\ndata Direction  = Up | Down\ntype Time       = Int\n```\n\n## Newtypes\n\nInstead of\n\n```purs\nfullName :: String -\u003e String -\u003e String\nfullName firstName lastName =\n  firstName \u003c\u003e \" \" \u003c\u003e lastName\n\nfullName \"Phillip\" \"Freeman\" -- \"Phillip Freeman\"\nfullName \"Freeman\" \"Phillip\" -- \"Freeman Phillip\" wrong order!\n```\n\nwe could write more explicit types but that would not prevent arguments ordering errors:\n\n```purs\ntype FirstName = String\ntype LastName = String\ntype FullName = String\n\nfullName :: FirstName -\u003e LastName -\u003e FullName\nfullName firstName lastName =\n  firstName \u003c\u003e \" \" \u003c\u003e lastName\n\nfullName \"Phillip\" \"Freeman\" -- \"Phillip Freeman\"\nfullName \"Freeman\" \"Phillip\" -- \"Freeman Phillip\" still wrong order!\n```\n\nInstead can use single constructor data types and destructure them to ensure the right arguments are provided:\n```purs\ndata FirstName = FirstName String\ndata LastName = LastName String\ndata FullName = FullName String\n\nfullName :: FirstName -\u003e LastName -\u003e FullName\nfullName (FirstName firstName) (LastName lastName) =\n  firstName \u003c\u003e \" \" \u003c\u003e lastName\n\nfullName (FirstName \"Phillip\") (LastName \"Freeman\") -- \"Phillip Freeman\"\nfullName (LastName \"Freeman\") (FirstName \"Phillip\") -- compiler error!\n```\n\nFor the compiler to optimize the output for this common pattern, it is even better to use the `newtype` keyword which is especially restricted to a single constructor which contains a single argument.\n\n```purs\nnewtype FirstName = FirstName String\nnewtype LastName = LastName String\nnewtype FullName = FullName String\n```\n\nNewtypes are especially useful when dealing with raw data as you can write a \"validation\" function without exposing the type constructor itself in exports. This is known as the [*smart constructor*](https://github.com/JordanMartinez/purescript-jordans-reference/blob/latestRelease/31-Design-Patterns/01-Smart-Constructors.md) pattern:\n\n```purs\nmodule Password\n  ( Password -- not Password(..) to prevent exposing the Password constructor\n  , toPassword\n  ) where\n\nnewtype Password = Password String\n\ntoPassword :: String -\u003e Either String Password\ntoPassword str =\n  if length str \u003e= 6 then\n    Right (Password str)\n  else\n    Left \"Size should be at least 6\"\n\nmyPassword = toPassword \"123456\"\n```\n\n## Modules\n\nHere is a full example shamelessly ripped off from the unmissable [*PureScript: Jordan's Reference*](https://jordanmartinez.github.io/purescript-jordans-reference-site/content/11-Syntax/04-Module-Syntax/src/11-Full-Module-Syntax-ps.html):\n\n```purs\nmodule Syntax.Module.FullExample\n  -- exports go here by just writing the name\n  ( value\n\n  , function, (\u003e@\u003e\u003e\u003e) -- aliases must be wrapped in parenthesis\n\n  -- when exporting type classes, there are two rules:\n  -- - you must precede the type class name with the keyword 'class'\n  -- - you must also export the type class' function (or face compilation errors)\n  , class TypeClass, tcFunction\n\n  -- when exporting modules, you must precede the module name with\n  -- the keyword 'module'\n  , module ExportedModule\n\n  -- The type is exported, but no one can create a value of it\n  -- outside of this module\n  , ExportDataType1_ButNotItsConstructors\n\n  -- syntax sugar for 'all constructors'\n  -- Either all or none of a type's constructors must be exported\n  , ExportDataType2_AndAllOfItsConstructors(..)\n\n  -- Type aliases can also be exported\n  , ExportedTypeAlias\n\n  -- When type aliases are aliased using infix notation, one must export\n  -- both the type alias, and the infix notation where 'type' must precede\n  -- the infix notation\n  , ExportedTypeAlias_InfixNotation, type (\u003c|\u003c\u003e|\u003e)\n\n  -- Data constructor alias; exporting the alias requires you\n  -- to also export the constructor it aliases\n  , ExportedDataType3_InfixNotation(Infix_Constructor), (\u003c||||\u003e)\n\n  , ExportedKind\n  , ExportedKindValue\n  ) where\n\n-- imports go here\n\n-- imports just the module\nimport Module\n\n-- import a submodule\nimport Module.SubModule.SubSubModule\n\n-- import values from a module\nimport ModuleValues (value1, value2)\n\n-- imports functions from a module\nimport ModuleFunctions (function1, function2)\n\n-- imports function alias from a module\nimport ModuleFunctionAliases ((/=), (===), (\u003e\u003e**\u003e\u003e))\n\n-- imports type class from the module\nimport ModuleTypeClass (class TypeClass)\n\n-- import a type but none of its constructors\nimport ModuleDataType (DataType)\n\n-- import a type and one of its constructors\nimport ModuleDataType (DataType(Constructor1))\n\n-- import a type and some of its constructors\nimport ModuleDataType (DataType(Constructor1, Constructor2))\n\n-- import a type and all of its constructors\nimport ModuleDataType (DataType(..))\n\n-- resolve name conflicts using \"hiding\" keyword\nimport ModuleNameClash1 (sameFunctionName1)\nimport ModuleNameClash2 hiding (sameFunctionName1)\n\n-- resolve name conflicts using module aliases\nimport ModuleNameClash1 as M1\nimport ModuleNameClash2 as M2\n\n-- Re-export modules\nimport Module1 (anInt1) as Exports\nimport Module2 (anInt2) as Exports\nimport Module3 (anInt3) as Exports\nimport Module4.SubModule1 (someFunction) as Exports\n\nimport ModuleKind (ImportedKind, ImportedKindValue) as Exports\n\nimport Prelude\n\nimport ExportedModule\n\n-- To prevent warnings from being emitted during compilation\n-- the above imports have to either be used here or\n-- re-exported (explained later in this folder).\n\nvalue :: Int\nvalue = 3\n\nfunction :: String -\u003e String\nfunction x = x\n\ninfix 4 function as \u003e@\u003e\u003e\u003e\n\nclass TypeClass a where\n  tcFunction :: a -\u003e a -\u003e a\n\n-- now 'sameFunctionName1' refers to ModuleF1's function, not ModuleF2's function\nmyFunction1 :: Int -\u003e Int\nmyFunction1 a = sameFunctionName1 a\n\nmyFunction2 :: Int -\u003e Int\nmyFunction2 a = M1.sameFunctionName1 (M2.sameFunctionName1 a)\n\ndataDifferences :: M1.SameDataName -\u003e M2.SameDataName -\u003e String\ndataDifferences M1.Constructor M2.Constructor = \"code works despite name clash\"\n\ndata ExportDataType1_ButNotItsConstructors = Constructor1A\n\ndata ExportDataType2_AndAllOfItsConstructors\n  = Constructor2A\n  | Constructor2B\n  | Constructor2C\n\ntype ExportedTypeAlias = Int\n\ndata ExportedDataType3_InfixNotation = Infix_Constructor Int Int\n\ninfixr 4 Infix_Constructor as \u003c||||\u003e\n\ntype ExportedTypeAlias_InfixNotation = String\n\ninfixr 4 type ExportedTypeAlias_InfixNotation as \u003c|\u003c\u003e|\u003e\n\ndata ExportedKind\n\nforeign import data ExportedKindValue :: ExportedKind\n```\n\n## Type classes\n\nThe `show` function takes a value and displays it as a string. `show` is defined by a type class in the Prelude module called Show, which is defined as follows:\n\n```purs\nclass Show a where\n  show :: a -\u003e String\n```\n\nThis code declares a new type class called `Show`, which is parameterized by the type variable `a`.\n\nA type class instance contains implementations of the functions defined in a type class, specialized to a particular type.\nYou can add any type to a class, as long as you define the required functions.\n\nFor example, here is the definition of the `Show` type class instance for `Boolean` values, taken from the Prelude.  We say that the `Boolean` type *belongs* to the `Show` *type class*.\n\n```purs\ninstance Show Boolean where\n  show true = \"true\"\n  show false = \"false\"\n```\n\nInstead of defining a different `map` for each type (Maybe, Result etc.) like in Elm, PureScript uses type classes.\n\nFor instance, `map` is defined once for all with the [`Functor`](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Functor) type class. A `Functor` is a type constructor which supports a mapping operation `map`.\n\n```purs\nclass Functor f where\n  map :: forall a b. (a -\u003e b) -\u003e f a -\u003e f b\n```\n\n## Type class deriving\n\nThe compiler can derive type class instances to spare you the tedium of writing boilerplate. There are a few ways to do this depending on the specific type and class being derived.\n\nSince PureScript version 0.15.0, giving class instances a name (for generated code readability) is optional. It it generated by the compiler if missing.\n\n### Classes with built-in compiler support\n\nSome classes have special built-in support (such as `Eq`), and their instances can be derived from all types.\n\nFor example, if you you'd like to be able to remove duplicates from an array of an ADT using `nub`, you need an `Eq` and `Ord` instance. Rather than writing these manually, let the compiler do the work.\n\n```purs\nimport Data.Array (nub)\n\ndata MyADT\n  = Some\n  | Arbitrary Int\n  | Contents Number String\n\nderive instance Eq MyADT\nderive instance Ord MyADT\n\nnub [Some, Arbitrary 1, Some, Some] == [Some, Arbitrary 1]\n```\n\nCurrently (in PureScript version 0.15.12), instances for the following classes can be derived by the compiler:\n- Data.Generic.Rep (class Generic) [see below](#deriving-from-generic)\n- [Data.Newtype (class Newtype)](https://pursuit.purescript.org/packages/purescript-newtype/docs/Data.Newtype#t:Newtype)\n- [Data.Eq (class Eq)](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Eq#t:Eq)\n- [Data.Ord (class Ord)](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Ord#t:Ord)\n- [Data.Functor (class Functor)](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Functor#t:Functor)\n- [Data.Functor.Contravariant (class Contravariant)](https://pursuit.purescript.org/packages/purescript-contravariant/docs/Data.Functor.Contravariant#t:Contravariant)\n- [Data.Bifunctor (class Bifunctor)](https://pursuit.purescript.org/packages/purescript-bifunctors/docs/Data.Bifunctor#t:Bifunctor)\n- [Data.Bifoldable (class Bifoldable)](https://pursuit.purescript.org/packages/purescript-foldable-traversable/docs/Data.Bifoldable#t:Bifoldable)\n- [Data.Bitraversable (class Bitraversable)](https://pursuit.purescript.org/packages/purescript-foldable-traversable/docs/Data.Bitraversable#t:Bitraversable)\n- [Data.Profunctor (class Profunctor)](https://pursuit.purescript.org/packages/purescript-profunctor/docs/Data.Profunctor#t:Profunctor)\n\n### Derive from `newtype`\n\nIf you would like your newtype to defer to the instance that the underlying type uses for a given class, then you can use newtype deriving via the `derive newtype` keywords.\n\nFor example, let's say you want to add two `Score` values using the `Semiring` instance of the wrapped `Int`.\n\n```purs\nnewtype Score = Score Int\n\nderive newtype instance Semiring Score\n\ntenPoints :: Score\ntenPoints = (Score 4) + (Score 6)\n```\n\nThat `derive` line replaced all this code:\n\n```purs\n-- No need to write this\ninstance Semiring Score where\n  zero = Score 0\n  add (Score a) (Score b) = Score (a + b)\n  mul (Score a) (Score b) = Score (a * b)\n  one = Score 1\n```\n\n[Data.Newtype](https://pursuit.purescript.org/packages/purescript-newtype/docs/Data.Newtype) provides useful functions via deriving newtypes instances:\n\n```purs\nimport Data.Newtype (Newtype, un)\n\nnewtype Address = Address String\nderive instance Newtype Address _\n\nprintAddress :: Address -\u003e Eff _ Unit\nprintAddress address = Console.log (un Address address)\n```\n\n### Deriving from `Generic`\n\nFor type classes without build-in support for deriving (such as `Show`) and for types other than newtypes where newtype deriving cannot be used, you can derive from `Generic` if the author of the type class library has implemented a generic version.\n\n```purs\nimport Data.Generic.Rep (class Generic)\nimport Data.Show.Generic (genericShow)\nimport Effect.Console (logShow)\n\nderive instance Generic MyADT _\n\ninstance Show MyADT where\n  show = genericShow\n\n-- logs `[Some,(Arbitrary 1),(Contents 2.0 \"Three\")]`\nmain = logShow [Some, Arbitrary 1, Contents 2.0 \"Three\"]\n```\n\n## Type class constraints\n\nHere is a type class constraint `Eq a`, separated from the rest of the type by a double arrow `=\u003e`:\n\n```purs\nthreeAreEqual :: forall a. Eq a =\u003e a -\u003e a -\u003e a -\u003e Boolean\nthreeAreEqual a1 a2 a3 = a1 == a2 \u0026\u0026 a2 == a3\n```\n\nThis type says that we can call `threeAreEqual` with any choice of type `a`, as long as there is an `Eq` instance available for `a`.\n\nMultiple constraints can be specified by using the `=\u003e` symbol multiple times:\n\n```purs\nshowCompare :: forall a. Ord a =\u003e Show a =\u003e a -\u003e a -\u003e String\nshowCompare a1 a2\n  | a1 \u003c a2 = show a1 \u003c\u003e \" is less than \" \u003c\u003e show a2\n  | a1 \u003e a2 = show a1 \u003c\u003e \" is greater than \" \u003c\u003e show a2\n  | otherwise = show a1 \u003c\u003e \" is equal to \" \u003c\u003e show a2\n```\n\nThe implementation of type class instances can depend on other type class instances. Those instances should be grouped in parentheses and separated by commas on the left-hand side of the `=\u003e` symbol:\n\n```purs\ninstance (Show a, Show b) =\u003e Show (Either a b) where\n  ...\n```\n\n## The Warn type class\n\nThere is a type class in `Prim` called `Warn`. When the compiler solves a `Warn` constraint it will trivially solve the instance and print out the message as a user defined warning.\n\n```purescript\nmeaningOfLife :: Warn (Text \"`meaningOfLife` result is hardcoded, for now.) =\u003e Int\nmeaningOfLife = 42\n```\n\n## Functors\n\n`\u003c$\u003e` is the infix alias of the `map` operator defined in the [Functor](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Functor) type class.\n\n```purs\nclass Functor f where\n  map :: forall a b. (a -\u003e b) -\u003e f a -\u003e f b\n```\n\nThe two following lines are equivalent:\n\n```purs\nmap (\\n -\u003e n + 1) (Just 5)\n(\\n -\u003e n + 1) \u003c$\u003e (Just 5)\n```\n\n## Applicatives\n\nTo *lift* a function means to turn it into a function that works with functor-wrapped arguments. Applicative functors are functors that allow lifting of functions.\n\n`\u003c*\u003e` is the infix alias of the `apply` operator defined in the [Apply](https://pursuit.purescript.org/packages/purescript-prelude/docs/Control.Apply) type class (that extends [`Functor`](https://pursuit.purescript.org/packages/purescript-prelude/docs/Data.Functor)). `\u003c*\u003e` is equivalent to [`|\u003e andMap`](https://thoughtbot.com/blog/running-out-of-maps#one-liner-to-rule-them-all) in Elm (with `andMap = Maybe.map2 (|\u003e)`).\n\nThe [Applicative](https://pursuit.purescript.org/packages/purescript-prelude/docs/Control.Applicative) type class extends the `Apply` type class with a `pure` function that takes a value and returns that value lifted into the applicative functor. With `Maybe`, `pure` is the same as `Just`, and with `Either`, `pure` is the same as `Right`, but it is recommended to use `pure` in case of an eventual applicative functor change.\n\n```purs\nclass Applicative f where\n  pure :: a -\u003e f a\n  apply :: f (a -\u003e b) -\u003e f a -\u003e f b -- \"inherited\" from the `Apply` type class\n```\n\nApplicative lets us perform N operations independently, then it aggregates the results for us.\nYou are in an applicative context when using [`Decoder`](https://package.elm-lang.org/packages/elm/json/latest/Json-Decode#map2) in Elm.\n\n### Applicative validation\n\nLet’s lift the function `fullName` over a `Maybe`:\n\n```purs\nimport Prelude\nimport Data.Maybe\n\nfullName :: String -\u003e String -\u003e String -\u003e String\nfullName first middle last = last \u003c\u003e \", \" \u003c\u003e first \u003c\u003e \" \" \u003c\u003e middle\n\nfullName \"Phillip\" \"A\" \"Freeman\" -- \"Freeman, Phillip A\"\n\nfullName \u003c$\u003e Just \"Phillip\" \u003c*\u003e Just \"A\" \u003c*\u003e Just \"Freeman\" -- Just (\"Freeman, Phillip A\")\n\nfullName \u003c$\u003e Just \"Phillip\" \u003c*\u003e Nothing \u003c*\u003e Just \"Freeman\" -- Nothing\n```\n\nJust like with `Maybe`, if we lift `fullName` over `Either String String`, we get a unique error even if multiple errors occur:\n\n```purs\nimport Test.Assert (assert)\nimport Data.Either (Either(..))\n\ntype Contact =\n  { firstName :: String\n  , lastName :: String\n  , address :: Address\n  }\n\ntype Address =\n  { street :: String\n  , city :: String\n  , country :: String\n  }\n\ngoodContact :: Contact\ngoodContact =\n  { firstName: \"John\"\n  , lastName: \"Doe\"\n  , address:\n      { street: \"123 Main St.\"\n      , city: \"Springfield\"\n      , country: \"USA\"\n      }\n  }\n\nbadContact :: Contact\nbadContact = goodContact { firstName = \"\", lastName = \"\" }\n\nnonEmptyEither :: String -\u003e String -\u003e Either String String\nnonEmptyEither fieldName value\n  | value == \"\" = Left $ \"Field '\" \u003c\u003e fieldName \u003c\u003e \"' cannot be empty\"\n  | otherwise = Right value\n\nvalidateContactEither :: Contact -\u003e Either String Contact\nvalidateContactEither c = { firstName: _, lastName: _, address: _ }\n  \u003c$\u003e nonEmptyEither \"First Name\" c.firstName\n  \u003c*\u003e nonEmptyEither \"Last Name\" c.lastName\n  -- lifting the `c.address` value into `Either` (we could also have used `Right c.address`)\n  \u003c*\u003e pure c.address\n\nassert $ validateContactEither goodContact == Right goodContact\nassert $ validateContactEither badContact ==  Left \"Field 'First Name' cannot be empty\"\n```\n\nTo get an array of all the errors we can use the `V` functor of [`Data.Validation.Semigroup`](https://pursuit.purescript.org/packages/purescript-validation/docs/Data.Validation.Semigroup) that it allows us to collect multiple errors using an arbitrary semigroup (such as `Array String` in the example below).\n\n```purs\nimport Data.Validation.Semigroup (V, invalid, isValid)\n\ntype ErrorMessages = Array String\n\nnonEmptyV :: String -\u003e String -\u003e V ErrorMessages String\nnonEmptyV fieldName value\n  | value == \"\" = invalid [ \"Field '\" \u003c\u003e fieldName \u003c\u003e \"' cannot be empty\" ]\n  | otherwise = pure value\n\nvalidateContactV :: Contact -\u003e V ErrorMessages Contact\nvalidateContactV c = { firstName: _, lastName: _, address: _ }\n  \u003c$\u003e nonEmptyV \"First Name\" c.firstName\n  \u003c*\u003e nonEmptyV \"Last Name\" c.lastName\n  \u003c*\u003e pure c.address\n\nassert $ isValid $ validateContactV goodContact\nassert $ not isValid $ validateContactV badContact\nassert $ validateContactV badContact ==\n  invalid\n    [ \"Field 'First Name' cannot be empty\"\n    , \"Field 'Last Name' cannot be empty\"\n    ]\n```\n\n### Applicative do notation\n\nWith the `ado` keyword:\n\n```purs\nvalidateContactVAdo :: Contact -\u003e V ErrorMessages Contact\nvalidateContactVAdo c = ado\n  fistName \u003c- nonEmptyV \"First Name\" c.firstName\n  lastName \u003c- nonEmptyV \"Last Name\" c.lastName\n  address \u003c- pure c.address\n  in { firstName, lastName, address }\n```\n\n## Monads\n\n`\u003e\u003e=` is the infix alias of the `bind` operator defined in the [Bind](https://pursuit.purescript.org/packages/purescript-prelude/docs/Control.Bind) type class (that extends [`Apply`](https://pursuit.purescript.org/packages/purescript-prelude/docs/Control.Apply)). `\u003e\u003e=` is equivalent to [`|\u003e andThen`](https://package.elm-lang.org/packages/elm/core/latest/Maybe#andThen) in Elm.\n\nThe [Monad](https://pursuit.purescript.org/packages/purescript-prelude/docs/Control.Applicative) type class combines the operations of the `Bind` and Applicative type classes. Therefore, `Monad` instances represent type constructors which support both sequential composition and function lifting.\n\n```purs\nclass Monad m where\n  bind :: m a -\u003e (a -\u003e m b) -\u003e m b\n```\n\nSo, to define a monad we need to define the `map`, `apply`, `pure` and `bind` operations:\n\n```purs\ndata Box a =\n  Box a\n\ninstance Functor Box where\n  map :: forall a b. (a -\u003e b) -\u003e Box a -\u003e Box  b\n  map f (Box a) = Box (f a)\n\ninstance Apply Box where\n  apply :: forall a b. Box (a -\u003e b) -\u003e Box a -\u003e Box  b\n  apply (Box f) (Box a) = Box (f a)\n\ninstance Applicative Box where\n  pure :: forall a. a -\u003e Box a\n  pure a = Box a\n\ninstance Bind Box where\n  bind :: forall a b. Box a -\u003e (a -\u003e Box b) -\u003e Box b\n  bind (Box a) f = f a\n\ninstance Monad Box\n```\n\nMonadic operations operate sequentially not concurrently. That’s great when we have a dependency between the operations e.g. lookup user_id based on email then fetch the inbox based on the user_id. But for independent operations monadic calls are very inefficient as they are inherently sequential. Monads fail fast which makes them poor for form validation and similar use cases. Once something \"fails\" the operation aborts.\nYou are in a monadic context when using [`Task`](https://package.elm-lang.org/packages/elm/core/latest/Task#andThen) in Elm.\n\n### Monad do-notation\n\nThe `do` keyword is \"syntactic sugar\" for chained `\u003e\u003e=`. It removes the need for indentations.\n\n```purs\nfoo :: Box Unit\nfoo =\n  -- only call `(\\x -\u003e ...)` if `getMyInt` actually produces something\n  getMyInt \u003e\u003e= (\\x -\u003e\n      let y = x + 4\n      in toString y \u003e\u003e= (\\z -\u003e\n        print z\n      )\n    )\n```\n\nis the same as\n\n```purs\nfoo :: Box Unit\nfoo = do\n  x \u003c- getMyInt\n  let y = x + 4 -- `in` keyword not needed\n  z \u003c- toString y\n  print z -- not `value \u003c- computation` but just `computation`\n```\n\n## Effects\n\nThe `main` function of PureScript programs uses the `Effect` monad for side effects:\n\n```purs\nimport Effect.Random (random) -- random :: Effect Number\n\nmain :: Effect Unit\nmain =\n  (log \"Below is a random number between 0.0 and 1.0:\") \u003e\u003e= (\\_ -\u003e\n    random \u003e\u003e= (\\n-\u003e\n      log $ show n\n    )\n  )\n```\n\nand is more readable using the do-notation:\n\n```purs\nmain :: Effect Unit\nmain = do\n  log \"Below is a random number between 0.0 and 1.0:\"\n  n \u003c- random\n  log $ show n\n```\n\nThe above example works because the last line has a `log` that returns `Effect Unit`.\nWe can use the [`void`](https://pursuit.purescript.org/packages/purescript-prelude/docs/Prelude#v:void) function to ignore the type wrapped by a Functor and replace it with `Unit`:\n\n```purs\nvoid :: forall f a. Functor f =\u003e f a -\u003e f Unit\n```\n\nThat is useful when using the do-notation:\n\n```purs\nmain :: Effect Unit\nmain = do\n  log \"Generating random number...\"\n  void random\n```\n\n## Asynchronous Effects (`Aff`)\n\nUsing asynchronous effects in PureScript is like using promises in JavaScript.\n\nPureScript applications use the `main` function in the context of the `Effect` monad. To start the `App` monad context from the `Effect` context, we use the [`launchAff`](https://pursuit.purescript.org/packages/purescript-aff/docs/Effect.Aff#v:launchAff) function (or `launchAff_`  which is `void $ launchAff`).\n\nWhen we have an Effect-based computation that we want to run in some other monadic context, we can use `liftEffect` from [Effect.Class](https://pursuit.purescript.org/packages/purescript-effect/docs/Effect.Class) if the target monad has an instance for `MonadEffect`:\n\n```purs\nclass (Monad m) ⇐ MonadEffect m where\n-- same as liftEffect :: forall a. Effect a -\u003e m a\n  liftEffect :: Effect ~\u003e m\n```\n\n`Aff` has an instance for `MonadEffect`, so we can lift `Effect`-based computations (such as `log`) into an `Aff` monadic context:\n\n```purs\nimport Prelude\n\nimport Effect (Effect)\nimport Effect.Aff (Milliseconds(..), delay, launchAff_)\nimport Effect.Class (liftEffect)\nimport Effect.Console (log)\nimport Effect.Timer (setTimeout, clearTimeout)\n\nmain :: Effect Unit\nmain = launchAff_ do\n  timeoutID \u003c- liftEffect $ setTimeout 1000 (log \"This will run after 1 second\")\n\n  delay (Milliseconds 1300.0)\n\n  liftEffect do\n    log \"Now cancelling timeout\"\n    clearTimeout timeoutID\n```\n\nWe can run multiple computations concurrently with [`forkAff`](https://pursuit.purescript.org/packages/purescript-aff/docs/Effect.Aff#v:forkAff). Then, we'll use [`joinFiber`](https://pursuit.purescript.org/packages/purescript-aff/docs/Effect.Aff#v:joinFiber) to ensure all computations are finished before we do another computation.\n\n```purs\nimport Effect (Effect)\nimport Effect.Aff (Milliseconds(..), delay, forkAff, joinFiber, launchAff_)\n\nmain :: Effect Unit\nmain = launchAff_ do\n\n  fiber1 \u003c- forkAff do\n    liftEffect $ log \"Fiber 1: Waiting for 1 second until completion.\"\n    delay $ Milliseconds 1000.0\n    liftEffect $ log \"Fiber 1: Finished computation.\"\n\n  fiber2 \u003c- forkAff do\n    liftEffect $ log \"Fiber 2: Computation 1 (takes 300 ms).\"\n    delay $ Milliseconds 300.0\n    liftEffect $ log \"Fiber 2: Computation 2 (takes 300 ms).\"\n    delay $ Milliseconds 300.0\n    liftEffect $ log \"Fiber 2: Computation 3 (takes 500 ms).\"\n    delay $ Milliseconds 500.0\n    liftEffect $ log \"Fiber 2: Finished computation.\"\n\n  fiber3 \u003c- forkAff do\n    liftEffect $ log \"Fiber 3: Nothing to do. Just return immediately.\"\n    liftEffect $ log \"Fiber 3: Finished computation.\"\n\n  joinFiber fiber1\n  liftEffect $ log \"Fiber 1 has finished. Now joining on fiber 2\"\n  joinFiber fiber2\n  liftEffect $ log \"Fiber 3 has finished. Now joining on fiber 3\"\n  joinFiber fiber3\n  liftEffect $ log \"Fiber 3 has finished. All fibers have finished their computation.\"\n```\n\nIf instead of `forkAff` we used [`suspendAff`](https://pursuit.purescript.org/packages/purescript-aff/docs/Effect.Aff#v:suspendAff), then the fibers would not be run *concurrently* as soon as defined, but they would be suspended and ran *sequentially* one by one after their respective `joinFiber`.\n\n## Foreign Function Interface (FFI)\n\n### Calling PureScript from JavaScript\n\n```purs\n-- Purescript\nmodule Tools where\n\nimport Prelude\n\n-- find the greatest common divisor\ngcd :: Int -\u003e Int -\u003e Int\ngcd n m\n  | n == 0 = m\n  | m == 0 = n\n  | n \u003e m = gcd (n - m) m\n  | otherwise = gcd (m - n) n\n```\n\nPureScript functions always get turned into Javascript functions of a single argument, so we need to apply its arguments one-by-one:\n\n```js\n// JavaScript\n\nimport { gcd } from 'Tools'\n\ngcd(15)(20)\n```\n\n### Calling Javascript from PureScript\n\n#### Foreign Modules\n\nA foreign module is just an ES module which is associated with a PureScript module.\n\n- All the ES module exports must be of the form `export const name = value` or `export function name() { ... }`.\n- The PureScript module must have the same as the ES one but with the `.purs` extension. It contains the signatures of the exports.\n\n##### Unary functions\n\n```js\n// JavaScript (src/Interest.js)\n\nexport function calculateInterest(amount) {\n  return amount * 0.1\n}\n```\n\n```purs\n-- PureScript (src/Interest.purs)\n\nmodule Interest where\n\nforeign import calculateInterest :: Number -\u003e Number\n```\n\n##### Functions of Multiple Arguments\n\nPureScript functions are curried by default, so Javascript functions of multiple arguments require special treatment.\n\n```js\n// JavaScript\n\nexport function calculateInterest(amount, months) {\n  return amount * Math.exp(0.1, months)\n}\n```\n\n```purs\n-- PureScript\n\nmodule Interest where\n\n-- available for function arities from 0 to 10\nimport Data.Function.Uncurried (Fn2)\n\nforeign import calculateInterest :: Fn2 Number Number Number\n```\n\nWe can write a curried wrapper function in PureScript which will allow partial application:\n\n```purs\ncalculateInterestCurried :: Number -\u003e Number -\u003e Number\ncalculateInterestCurried = runFn2 calculateInterest\n```\n\nAn alternative is to use curried functions in the native module, using multiple nested functions, each with a single argument:\n\n```js\n// JavaScript\n\nexport const calculateInterest = amount =\u003e months =\u003e amount * Math.exp(0.1, months)\n```\n\nThis time, we can assign the curried function type directly:\n\n```purs\n-- PureScript\n\nforeign import calculateInterest :: Number -\u003e Number -\u003e Number\n```\n\n#### Promises\n\nPromises in JavaScript translate directly to asynchronous effects in PureScript with the help of [`Promise.Aff`](https://pursuit.purescript.org/packages/purescript-js-promise-aff/docs/Promise.Aff).\n\nIn JavaScript, you need to wrap asynchronous functions in a PureScript Effect with a \"thunk\" `() =\u003e` so the function is not considered pure and is run every time:\n\n```js\n// JavaScript\n\nexport const catBase64JS = text =\u003e fontSize =\u003e\n    async () =\u003e {\n        const response = await fetch(`https://cataas.com/cat/says/${text}?fontSize=${fontSize}\u0026fontColor=red`)\n        const array = await response.body.getReader().read()\n        return btoa(String.fromCharCode.apply(null, array.value))\n    }\n```\n\nThen in PureScript use the `toAffE` function:\n\n```purs\n-- PureScript\n\nimport Promise.Aff (Promise, toAffE)\n\nforeign import catBase64JS :: String -\u003e Int -\u003e Effect (Promise String)\n\ncatBase64 :: String -\u003e Int -\u003e Aff String\ncatBase64 text fontSize = toAffE $ catBase64JS text fontSize\n```\n\n## Sanitizing Foreign Data\n\nIt is important to sanitize data when working with values returned from Javascript functions using the FFI. For this we will use  [`purescript-foreign-generic`](https://pursuit.purescript.org/packages/purescript-foreign-generic).\n\n\n```purs\nimport Data.Foreign\nimport Data.Foreign.Generic\nimport Data.Foreign.JSON\n```\n\n`purescript-foreign-generic` has the following functions:\n\n```purs\nparseJSON :: String -\u003e F Foreign\ndecodeJSON :: forall a. Decode a =\u003e String -\u003e F a\n```\n\n`F` is a type alias:\n\n```purs\ntype F = Except (NonEmptyList ForeignError)\n```\n\nNote: [The usage of the `F` alias is now discouraged](https://github.com/purescript/documentation/blob/master/migration-guides/0.15-Migration-Guide.md#discouraging-usage-of-f-and-ft).\n\n`Except` is an monad for handling exceptions, much like `Either`. We can convert a value in the `F` monad into a value in the `Either` monad by using the `runExcept` function.\n\n```purs\nimport Control.Monad.Except\n\nrunExcept (decodeJSON \"\\\"Testing\\\"\" :: F String)\n-- Right \"Testing\"\n\nrunExcept (decodeJSON \"true\" :: F Boolean)\n-- Right true\n\nrunExcept (decodeJSON \"[1, 2, 3]\" :: F (Array Int))\n-- Right [1, 2, 3]\n\nrunExcept (decodeJSON \"[1, 2, true]\" :: F (Array Int))\n-- Left (NonEmptyList (NonEmpty (ErrorAtIndex 2 (TypeMismatch \"Int\" \"Boolean\")) Nil))\n```\n\nReal-world JSON documents contain null and undefined values, so we need to be able to handle those too.\n\n`purescript-foreign-generic` defines a type constructors which solves this problem: `NullOrUndefined`. It uses the `Maybe` type constructor internally to represent missing values.\n\nThe module also provides a function `unNullOrUndefined` to unwrap the inner value. We can lift the appropriate function over the `decodeJSON` action to parse JSON documents which permit null values:\n\n```purs\nimport Data.Foreign.NullOrUndefined\n\nrunExcept (unNullOrUndefined \u003c$\u003e decodeJSON \"42\" :: F (NullOrUndefined Int))\n-- Right (Just 42)\n\nrunExcept (unNullOrUndefined \u003c$\u003e decodeJSON \"null\" :: F (NullOrUndefined Int))\n-- Right Nothing\n```\n\nTo parse arrays of integers where each element might be null, we can lift the function `map unNullOrUndefined` over the `decodeJSON` action:\n\n```purs\nrunExcept (map unNullOrUndefined \u003c$\u003e decodeJSON \"[1, 2, null]\" :: F (Array (NullOrUndefined Int)))\n-- Right [(Just 1),(Just 2),Nothing]\n```\n\n## Front-end frameworks\n\nIn [The state of PureScript 2023 survey results](https://github.com/purescript/survey/blob/main/results/The%20State%20of%20PureScript%202023%20Results.pdf), at page 24, you can see a chart of the most used front-end frameworks:\n\n\u003cimg src=\"ps-frameworks-2023.png\" alt=\"PureScript frameworks usage chart for 2023\" width=\"600\"\u003e\n\n- [**Halogen**](https://purescript-halogen.github.io/purescript-halogen/) is by far the most used front-end framework for PureScript.\n  - *Not* using [The Elm Architecture](https://guide.elm-lang.org/architecture/) (\"TEA\").\n  - You can create components if you’re into that stuff. There is [purescript-halogen-store](https://github.com/thomashoneyman/purescript-halogen-store) for global state management.\n  - Does not have a router. You will have to use [purescript-routing](https://github.com/purescript-contrib/purescript-routing) or [purescript-routing-duplex](https://github.com/natefaubion/purescript-routing-duplex).\n  - [A bit slower](https://github.com/purescript-halogen/purescript-halogen/issues/632#issuecomment-609952547) than Elm.\n  - About twice heavier than Elm with brotli compression for a real world app.\n\n\n- [**Elmish**](https://collegevine.github.io/purescript-elmish/), as its name suggests, uses Elm ideas:\n  - (loosely) follows TEA principles, implemented as a thin layer on top of React.\n  - Has [routing capabilities](https://pursuit.purescript.org/packages/purescript-elmish/0.1.0/docs/Elmish.Component#t:ComponentReturnCallback).\n  - Bloated with React 17 (I couldn’t figure out how to get it to work with [Preact](https://preactjs.com/)).\n  - Seems abandoned as the package maintainer [no longer has the motivation to update it to React 18 and above](https://github.com/collegevine/purescript-elmish/pull/66#issuecomment-1810431289).\n\n- [**Flame**](https://flame.asafe.dev/) is a relatively new framework inspired by Elm:\n\n  - The [Flame architecture](https://flame.asafe.dev/events) is inspired by TEA.\n  - Does not have a router. You will have to use [purescript-routing](https://github.com/purescript-contrib/purescript-routing) or [purescript-routing-duplex](https://github.com/natefaubion/purescript-routing-duplex).\n  - Performance is [comparable to Elm](https://flame.asafe.dev/benchmarks).\n  - Server Side Rendering is [supported](https://flame.asafe.dev/rendering).\n\n### Flame example\n\nThis repo contains a minimal Flame example with a counter increment/decrement buttons, random number generation, synchronous and asynchronous FFI calls, subscription and decoding of a JSON object.\n\n#### Installation\n\n```bash\nnpm i\n```\n\n#### Vite setup notes\n\n- When using [PureScript IDE for VS code](https://marketplace.visualstudio.com/items/nwolverson.ide-purescript) the project is built every time you save a file. There is no need for a special Vite plugin. `output/Main/index.js` is simply imported in the JavasScript entry file.\n- [Terser](https://terser.org/) is used for better compression results.\n\n\n## Go further\n\nWe only covered the basics of PureScript here. If you want to learn more, check out the following resources:\n\n- [Official documentation](https://github.com/purescript/documentation) of course.\n- [PureScript: Jordan's Reference](https://jordanmartinez.github.io/purescript-jordans-reference-site/) is a must.\n- [PureScript By Example](https://book.purescript.org/) is a free online book containing several practical projects for PureScript beginners.\n- [Functional Programming Made Easier](https://leanpub.com/fp-made-easier) by Charles Scalfani is a great book to learn PureScript.\n- [PureScript Discourse](https://discourse.purescript.org/) forum\n- [PureScript Discord](https://purescript.org/chat) chat\n\n## License\n\n[MIT](https://github.com/laurentpayot/purescript-for-elm-developers/blob/main/LICENSE)\n\n## Stargazers :heart:\n\n[![Stargazers repo roster for @laurentpayot/purescript-for-elm-developers](http://reporoster.com/stars/laurentpayot/purescript-for-elm-developers)](https://github.com/laurentpayot/purescript-for-elm-developers/stargazers)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaurentpayot%2Fpurescript-for-elm-developers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flaurentpayot%2Fpurescript-for-elm-developers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flaurentpayot%2Fpurescript-for-elm-developers/lists"}