{"id":27416162,"url":"https://github.com/thimoteus/purescript-transformerless","last_synced_at":"2026-01-21T09:01:34.501Z","repository":{"id":58222685,"uuid":"61249755","full_name":"Thimoteus/purescript-transformerless","owner":"Thimoteus","description":"Transformerless monads for Puresript","archived":false,"fork":false,"pushed_at":"2018-09-26T18:14:27.000Z","size":42,"stargazers_count":22,"open_issues_count":1,"forks_count":3,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-04-12T14:50:30.519Z","etag":null,"topics":["monad-transformers","purescript","transformer"],"latest_commit_sha":null,"homepage":null,"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/Thimoteus.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}},"created_at":"2016-06-16T00:23:35.000Z","updated_at":"2022-04-23T09:24:33.000Z","dependencies_parsed_at":"2022-08-31T03:00:35.733Z","dependency_job_id":null,"html_url":"https://github.com/Thimoteus/purescript-transformerless","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thimoteus%2Fpurescript-transformerless","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thimoteus%2Fpurescript-transformerless/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thimoteus%2Fpurescript-transformerless/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Thimoteus%2Fpurescript-transformerless/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Thimoteus","download_url":"https://codeload.github.com/Thimoteus/purescript-transformerless/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248858089,"owners_count":21173109,"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":["monad-transformers","purescript","transformer"],"created_at":"2025-04-14T09:46:51.335Z","updated_at":"2026-01-21T09:01:34.459Z","avatar_url":"https://github.com/Thimoteus.png","language":"PureScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# purescript-transformerless\n\n## Why?\nIn Haskell and Purescript, the standard `Writer`, `Reader`, `State` and `RWS`\nmonads are implemented in terms of their monad *transformer* versions over the\n`Identity` monad. Depending on how you learned about monad transformers, you might\nremember reading something like the following:\n\n\u003e The State/Reader/Writer monad is ...\n\u003e\n\u003e A monad transformer is ...\n\u003e\n\u003e The StateT/ReaderT/WriterT monad transformer is ...\n\u003e\n\u003e In fact, the State/Reader/Writer monad from section {3 lines ago} is actually\ndefined as StateT s Identity/ReaderT r Identity/WriterT w Identity!\n\nWow, what a plot twist!\n\nHowever, for all the theoretical cleanliness, it's \"common knowledge\" among\nPurescripters that transformer stacks are slow and generate some funky Javascript.\n\n## Usage\n\nThe same as a normal `State`, `Reader`, etc. However, you should know that\nnone of these types have instances for their respective transformer counterparts:\nthere is no instance for `..State.Class.MonadState s (..Transformerless.State s)`\nor its buddies. Wouldn't it be weird for a package called \"transformerless\" to\ndepend on a package called \"transformers\"?\n\nAs a result, a \"transformers\" typeclass function is just a normal function in\nthe transformerless counterpart's module.\n\n## Scrap Your Typeclasses\n\nEach module also exports normal functions corresponding to typeclass members for\neach typeclass instance in the module.\n\n`Control.Monad.Transformerless.Reader` exports `mapR, applyR, pureR, bindR`\nas well as infix aliases `|-\u003e, ~, \u003e\u003e-` for `mapR, applyR, bindR` respectively.\n\n`Writer` and `State` are similar, but `RWS` exports `map_, apply_, pure_, bind_`.\nHowever, the aliases are the same in each module.\n\nUsing these instead of their overloaded versions avoids passing typeclass\ndictionaries, and could result in a speedup.\n\n## Questions I'll Ask and Answer for You\n\n### You mentioned code generation. Is it really better for this package?\n\nExamples:\n\nGenerating code for this transformers code:\n```purescript\nloop :: Int -\u003e RWST String (Array String) Int Identity Unit\nloop n = tailRecM go n\n  where\n  go 0 = do\n    tell [ \"Done!\" ]\n    pure (Right unit)\n  go n = do\n    x \u003c- get\n    put (x + 1)\n    pure (Left (n - 1))\n```\n\nresults in this javascript:\n\n```javascript\nvar loop = function (n) {\n    var go = function (v) {\n        if (v === 0) {\n            return Control_Bind.discard(Control_Bind.discardUnit)(Control_Monad_RWS_Trans.bindRWST(Data_Identity.bindIdentity)(Data_Monoid.monoidArray))(Control_Monad_Writer_Class.tell(Control_Monad_RWS_Trans.monadTellRWST(Data_Identity.monadIdentity)(Data_Monoid.monoidArray))([ \"Done!\" ]))(function () {\n                return Control_Applicative.pure(Control_Monad_RWS_Trans.applicativeRWST(Data_Identity.monadIdentity)(Data_Monoid.monoidArray))(new Control_Monad_Rec_Class.Done(Data_Unit.unit));\n            });\n        };\n        return Control_Bind.bind(Control_Monad_RWS_Trans.bindRWST(Data_Identity.bindIdentity)(Data_Monoid.monoidArray))(Control_Monad_State_Class.get(Control_Monad_RWS_Trans.monadStateRWST(Data_Identity.monadIdentity)(Data_Monoid.monoidArray)))(function (v1) {\n            return Control_Bind.discard(Control_Bind.discardUnit)(Control_Monad_RWS_Trans.bindRWST(Data_Identity.bindIdentity)(Data_Monoid.monoidArray))(Control_Monad_State_Class.put(Control_Monad_RWS_Trans.monadStateRWST(Data_Identity.monadIdentity)(Data_Monoid.monoidArray))(v1 + 1 | 0))(function () {\n                return Control_Applicative.pure(Control_Monad_RWS_Trans.applicativeRWST(Data_Identity.monadIdentity)(Data_Monoid.monoidArray))(new Control_Monad_Rec_Class.Loop(v - 1 | 0));\n            });\n        });\n    };\n    return Control_Monad_Rec_Class.tailRecM(Control_Monad_RWS_Trans.monadRecRWST(Control_Monad_Rec_Class.monadRecIdentity)(Data_Monoid.monoidArray))(go)(n);\n};\n```\n\nvs this transformerless code:\n```purescript\nloop :: Int -\u003e RWS.RWS String (Array String) Int Unit\nloop n = RWS.tailRec_ go n where\n  go 0 = do\n    _ \u003c- RWS.tell [\"Done!\"]\n    RWS.pure_ (Done unit)\n      where\n        bind = RWS.bind_\n  go m = do\n    x \u003c- RWS.get\n    _ \u003c- RWS.put (x + 1)\n    RWS.pure_ (Loop (m - 1))\n      where\n        bind = RWS.bind_\n```\n\nwith this javascript:\n```javascript\nvar loop = function (n) {\n    var go = function (v) {\n        if (v === 0) {\n            return Control_Monad_Transformerless_RWS.bind_(Data_Semigroup.semigroupArray)(Control_Monad_Transformerless_RWS.tell([ \"Done!\" ]))(function (v1) {\n                return Control_Monad_Transformerless_RWS.pure_(Data_Monoid.monoidArray)(new Control_Monad_Rec_Class.Done(Data_Unit.unit));\n            });\n        };\n        return Control_Monad_Transformerless_RWS.bind_(Data_Semigroup.semigroupArray)(Control_Monad_Transformerless_RWS.get(Data_Monoid.monoidArray))(function (v1) {\n            return Control_Monad_Transformerless_RWS.bind_(Data_Semigroup.semigroupArray)(Control_Monad_Transformerless_RWS.put(Data_Monoid.monoidArray)(v1 + 1 | 0))(function (v2) {\n                return Control_Monad_Transformerless_RWS.pure_(Data_Monoid.monoidArray)(new Control_Monad_Rec_Class.Loop(v - 1 | 0));\n            });\n        });\n    };\n    return Control_Monad_Transformerless_RWS.tailRec_(Data_Monoid.monoidArray)(go)(n);\n};\n```\n\n### What about speed?\n\nOn my computer, testing the above `loop` functions 10,000,000 times. The transformerless version is on the right, and\ntiming the `loop` function is labeled \"RWS\". The transformer version is labeled \"RWST\":\n\n![test](http://i.imgur.com/Fww56is.png)\n\nAnd if you can't read my font, it says `RWST: 38929.0` and `RWS: 15048.0`.\n\n## Installing\n`bower i purescript-transformerless`\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthimoteus%2Fpurescript-transformerless","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthimoteus%2Fpurescript-transformerless","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthimoteus%2Fpurescript-transformerless/lists"}