{"id":13709384,"url":"https://github.com/aristanetworks/purescript-backend-optimizer","last_synced_at":"2026-01-30T20:32:52.798Z","repository":{"id":57900690,"uuid":"528101408","full_name":"aristanetworks/purescript-backend-optimizer","owner":"aristanetworks","description":"Optimizing backend toolkit and modern ECMAScript backend for PureScript","archived":false,"fork":false,"pushed_at":"2025-12-24T16:26:10.000Z","size":713,"stargazers_count":210,"open_issues_count":18,"forks_count":21,"subscribers_count":15,"default_branch":"main","last_synced_at":"2025-12-26T07:27:04.342Z","etag":null,"topics":["optimizer","purescript"],"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/aristanetworks.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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-08-23T17:43:06.000Z","updated_at":"2025-12-24T16:26:13.000Z","dependencies_parsed_at":"2023-10-01T17:21:58.644Z","dependency_job_id":"c0c97410-1551-440b-9aa6-ff5d5939f3c1","html_url":"https://github.com/aristanetworks/purescript-backend-optimizer","commit_stats":{"total_commits":224,"total_committers":9,"mean_commits":24.88888888888889,"dds":0.3839285714285714,"last_synced_commit":"b967c009a924aba4d448d8f0a93056cfa285d118"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/aristanetworks/purescript-backend-optimizer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aristanetworks%2Fpurescript-backend-optimizer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aristanetworks%2Fpurescript-backend-optimizer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aristanetworks%2Fpurescript-backend-optimizer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aristanetworks%2Fpurescript-backend-optimizer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aristanetworks","download_url":"https://codeload.github.com/aristanetworks/purescript-backend-optimizer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aristanetworks%2Fpurescript-backend-optimizer/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28918453,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-30T20:25:28.696Z","status":"ssl_error","status_checked_at":"2026-01-30T20:25:13.426Z","response_time":66,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["optimizer","purescript"],"created_at":"2024-08-02T23:00:38.688Z","updated_at":"2026-01-30T20:32:52.789Z","avatar_url":"https://github.com/aristanetworks.png","language":"PureScript","readme":"# purescript-backend-optimizer\n\nAn optimizing backend toolkit for PureScript's CoreFn.\n\n## Overview\n\nPureScript's built-in optimizer leaves a lot on the table by only performing\nnaive syntactic rewrites in the JavaScript specific backend.\n`purescript-backend-optimizer` consumes the compiler's high-level IR (`CoreFn`)\nand applies a more aggressive inlining pipeline (subsuming existing\noptimizations) that is backend agnostic.\n\nIt additionally ships with an alternative code-generator which outputs modern\nECMAScript with additional runtime optimizations, resulting in lighter, faster\nbundles.\n\n| Example | Input | `purs` | `purs-backend-es` |\n|---------|-------|--------|-------------------|\n| Lenses | [Input](./backend-es/test-snapshots/src/Snapshot.ProfunctorLenses01.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.ProfunctorLenses01.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.ProfunctorLenses01.js) |\n| Prisms | [Input](./backend-es/test-snapshots/src/Snapshot.ProfunctorLenses02.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.ProfunctorLenses02.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.ProfunctorLenses02.js) |\n| Variant | [Input](./backend-es/test-snapshots/src/Snapshot.Variant01.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.Variant01.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.Variant01.js) |\n| Heterogeneous | [Input](./backend-es/test-snapshots/src/Snapshot.Heterogeneous01.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.Heterogeneous01.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.Heterogeneous01.js) |\n| Uncurried CPS | [Input](./backend-es/test-snapshots/src/Snapshot.Cps02.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.Cps02.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.Cps02.js) |\n| Generics | [Input](./backend-es/test-snapshots/src/Snapshot.KnownConstructors06.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.KnownConstructors06.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.KnownConstructors06.js) |\n| Fusion (Fold) | [Input](./backend-es/test-snapshots/src/Snapshot.Fusion01.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.Fusion01.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.Fusion01.js) |\n| Fusion (Unfold) | [Input](./backend-es/test-snapshots/src/Snapshot.Fusion02.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.Fusion02.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.Fusion02.js) |\n| Recursion Schemes | [Input](./backend-es/test-snapshots/src/Snapshot.RecursionSchemes01.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.RecursionSchemes01.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.RecursionSchemes01.js) |\n| HTML DSL | [Input](./backend-es/test-snapshots/src/Snapshot.Html.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.Html.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.Html.js) |\n| Imperative Loops | [Input](./backend-es/test-snapshots/src/Snapshot.HalogenVDomST01.purs) | [Output](https://try.purescript.org/?view=output\u0026js=true\u0026github=aristanetworks/purescript-backend-optimizer/main/backend-es/test-snapshots/src/Snapshot.HalogenVDomST01.purs) | [Output](./backend-es/test-snapshots/snapshots-out/Snapshot.HalogenVDomST01.js) |\n\n\n\n## ECMAScript Backend\n\n### Install\n\n```sh\nnpm install purs-backend-es\n```\n\n### Usage\n\n`purs-backend-es` requires PureScript `0.15.4` or greater. Add it as a backend in your `spago.dhall`.\n\n```diff\n+, backend = \"purs-backend-es build\"\n```\n\n_You should likely only do this for a production build configuration_, since\noptimization and code-generation are currently not incremental. For example,\nyou can create a separate `prod.dhall` with the following:\n\n```dhall\n./spago.dhall // { backend = \"purs-backend-es build\" }\n```\n\nBy default, `purs-backend-es` will read corefn.json files from `output`, and\ngenerate code in `output-es` following the same directory pattern as the\ncompiler's JS backend.\n\nSee the CLI help for options:\n\n```sh\npurs-backend-es --help\n```\n\n`spago bundle-app` is not compatible with `purs-backend-es`. To bundle your app,\nyou can invoke `purs-backend-es bundle-app`. It supports the same CLI arguments\nas `spago bundle-app`.\n\n```sh\nspago build \u0026\u0026 purs-backend-es bundle-app --no-build\n```\n\n### Notable Differences from `purs`\n\n* Uses arrow functions, `const/let` block scope, and object spread syntax.\n* Uses a much lighter-weight data encoding (using plain objects) which is significantly\n  faster to dispatch. By default, we use string tags, but integer tags are also\n  supported via a CLI flag for further performance improvement and size reduction.\n* Newtypes over `Effect` and `ST` also benefit from optimizations. With general\n  inlining, even instances that aren't newtype-derived benefit from the same\n  optimizations.\n* TCO fires in more cases. For example, you can now write TCO loops over\n  `purescript-exists` because the eliminator is inlined away.\n* TCO supports mutually recursive binding groups.\n* Optimized pattern matching eliminates redundant tests.\n\nCode size and performance improvement varies by usecase, but we've generally\nobserved:\n\n* 25-35% improvement in runtime.\n* 20-25% improvement in minified bundle size.\n* 15-20% improvement in minified+gzip bundle size.\n\n## Inlining Directives\n\nThe inliner follows some basic heuristics, but to get the most out of it you\nshould configure inlining directives. An inlining directive tells the optimizer\nunder what conditions it should inline a definition.\n\nThe following inlining directives are supported:\n\n  * `default` - A definition is inlined using default heuristics (unspecified).\n  * `never` - A definition is never inlined.\n  * `always` - A definition is inlined at every reference.\n  * `arity=n` - Where `n` is a positive integer, a definition is inlined when\n    at least `n` arguments are applied.\n\nAn inlining directive may be applied to a top-level binding or top-level accessor.\n\n### Syntax\n\n```purescript\nmodule Example where\n\nimport Prelude\n\nmyAdd :: Int -\u003e Int -\u003e Int\nmyAdd a b = a + b\n```\n\nThe `myAdd` function would likely already be inlined since it is so small, but\nto guarantee that it is always inlined after two arguments are applied, you would\nwrite the following directive:\n\n```\nExample.myAdd arity=2\n```\n\nFor instance methods, you should use named instances and a top-level accessor:\n\n```purescript\nmodule Example where\n\nimport Prelude\n\ndata MyData = Zero | One\n\ninstance semigroupMyData :: Semigroup MyData where\n  append = case _, _ of\n    Zero, _ -\u003e Zero\n    _, Zero -\u003e Zero\n    _, _ -\u003e One\n```\n\n```\nExample.semigroupMyData.append arity=2\n```\n\nIt's possible to refer to unnamed instances through their compiler-generated\nname, however this is quite difficult to maintain.\n\nSometimes instances are parameterized by other constraints:\n\n```purescript\nmodule Example where\n\nimport Prelude\n\ndata Product f g a = Product (f a) (g a)\n\ninstance functorProduct :: (Functor f, Functor g) =\u003e Functor (Product f g) where\n  map f (Product a b) = Product (f \u003c$\u003e a) (f \u003c$\u003e b)\n```\n\n```\nExample.functorProduct(..).map arity=2\n```\n\nNote the `(..)` between the name and the accessor, which will match\napplications of known instance dictionaries.\n\n### Configuration\n\nInlining directives can be configured in three ways:\n\n#### Module-specific inlining directives via a module header\n\nIn any given module header you can add `@inline` comments with the above syntax:\n\n```purescript\n-- @inline Example.myAdd arity=2\nmodule AnotherExample where\n\nimport Example\n...\n```\n\nDirectives configured this way only apply to the current module.\n\n#### Global inlining directives via a module header\n\nIn any given module header, you can add `@inline export` directives for definitions\nin the current module:\n\n```purescript\n-- @inline export myAdd arity=2\n-- @inline export semigroupMyData.append arity=1\nmodule Example where\n...\n```\n\nDirectives configured this way apply to the current module and downstream\nmodules.\n\n*Note:* They must be defined in the module header to due to an upstream compiler\nlimitation.\n\n#### Global inlining directives via a configuration file\n\nYou can provide a directive file to `purs-backend-es`:\n\n```sh\npurs-backend-es build --directives my-directives.txt\n```\n\nEach line should contain an inlining directive using the above syntax, with the\nadditional support of `--` line comments. These directives will take precedence\nover any defaults or exported directives, so you can tweak inlining for your\nproject however you see fit.\n\n#### Cheatsheet\n\nPrecedence applies in the following order (most specific to least specific):\n\n| Location | Affects |\n|----------|---------|\n| Module A's header, `@inline` module B directive | Module B's usages in module A |\n| Directives file | All modules |\n| Module A's header, `@inline export` module A directive | Module A's usages in all modules |\n| Default heuristics | All modules |\n\n## Tracing Optimizations\n\n`purs-backend-es` can also trace the rewrite passes taken when optimizing a\ntop-level expression via the `--trace-rewrites` CLI arg. This may help in\ndebugging an unexpected or non-optimal result.\n\n## Semantics\n\n`purescript-backend-optimizer` consumes the PureScript compiler's high-level\nintermediate representation (IR) known as CoreFn. CoreFn has no defined\nevaluation semantics, but we operate under assumptions based on common use:\n\n* We make decisions on what to keep or discard using\n  [Fast and Loose\n  Reasoning](https://www.cs.ox.ac.uk/jeremy.gibbons/publications/fast+loose.pdf),\n  assuming that CoreFn is _pure_ and _total_.\n\n  In practical terms, this means we _may_ take the opportunity to remove any\n  code that we know for certain is not demanded. However, at times we may also\n  choose to propagate known bottoms. Thus, non-totality is considered _undefined\n  behavior_ for the purposes of CoreFn's semantics.\n\n* We preserve _sharing_ of redexes under common assumptions of call-by-value semantics.\n  Like non-totality, we consider a specific evaluation order to be _undefined\n  behavior_ in CoreFn. However, we assume that all terms under a redex should be\n  in normal form.\n\n  In practical terms, this means we will not delay function arguments that most\n  would expect to be evaluated immediately.\n","funding_links":[],"categories":["Essential tools"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faristanetworks%2Fpurescript-backend-optimizer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faristanetworks%2Fpurescript-backend-optimizer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faristanetworks%2Fpurescript-backend-optimizer/lists"}