{"id":20834445,"url":"https://github.com/qzchenwl/more-extensible-effects","last_synced_at":"2025-08-23T03:14:16.197Z","repository":{"id":56847484,"uuid":"78993462","full_name":"qzchenwl/more-extensible-effects","owner":"qzchenwl","description":"An implementation of \"Freer Monads, More Extensible Effects\".","archived":false,"fork":false,"pushed_at":"2017-01-29T12:01:27.000Z","size":39,"stargazers_count":7,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-08T01:45:09.886Z","etag":null,"topics":["extensible-effects","free-monad","haskell"],"latest_commit_sha":null,"homepage":null,"language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qzchenwl.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":"2017-01-15T03:49:53.000Z","updated_at":"2023-12-31T01:23:05.000Z","dependencies_parsed_at":"2022-08-30T20:10:35.331Z","dependency_job_id":null,"html_url":"https://github.com/qzchenwl/more-extensible-effects","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qzchenwl%2Fmore-extensible-effects","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qzchenwl%2Fmore-extensible-effects/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qzchenwl%2Fmore-extensible-effects/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qzchenwl%2Fmore-extensible-effects/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qzchenwl","download_url":"https://codeload.github.com/qzchenwl/more-extensible-effects/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252983757,"owners_count":21835759,"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":["extensible-effects","free-monad","haskell"],"created_at":"2024-11-18T00:19:18.093Z","updated_at":"2025-05-08T01:45:17.369Z","avatar_url":"https://github.com/qzchenwl.png","language":"Haskell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# More Extensible Effects\n\nThis package is an implementation of [\"Freer Monads, More Extensible Effects\"](http://okmij.org/ftp/Haskell/extensible/more.pdf).\n\nMuch of the implementation is a repackaging and cleaning up of the reference materials provided [here](http://okmij.org/ftp/Haskell/extensible/):\n\n- [Eff1.hs](http://okmij.org/ftp/Haskell/extensible/Eff1.hs)\n- [OpenUnion5.hs](http://okmij.org/ftp/Haskell/extensible/OpenUnion5.hs)\n- [FTCQueue1.hs](http://okmij.org/ftp/Haskell/extensible/FTCQueue1.hs)\n\n## Overview\n\n- Control\n  - Monad\n    - [Eff](src/Control/Monad/Eff.hs)\n      - [Examples](src/Control/Monad/Eff/Examples.hs)\n        - [Teletype](src/Control/Monad/Eff/Examples/Teletype.hs)\n        - [VerboseAddition](src/Control/Monad/Eff/Examples/VerboseAddition.hs)\n      - [Exception](src/Control/Monad/Eff/Exception.hs)\n      - [Internal](src/Control/Monad/Eff/Internal.hs)\n      - [Lift](src/Control/Monad/Eff/Lift.hs)\n      - [NdetEff](src/Control/Monad/Eff/NdetEff.hs)\n      - [Reader](src/Control/Monad/Eff/Reader.hs)\n      - [State](src/Control/Monad/Eff/State.hs)\n      - [StateRW](src/Control/Monad/Eff/StateRW.hs)\n      - [Writer](src/Control/Monad/Eff/Writer.hs)\n- Data\n  - [FTCQueue](src/Data/FTCQueue.hs)\n  - [OpenUnion](src/Data/OpenUnion.hs)\n\n## Examples\n\n### Log Effect ([24 Days of Hackage: extensible-effects](https://ocharles.org.uk/blog/posts/2013-12-04-24-days-of-hackage-extensible-effects.html))\n\n[VerboseAddition.hs](src/Control/Monad/Eff/Examples/VerboseAddition.hs)\n\n```haskell\n{-# LANGUAGE DataKinds #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE GADTs #-}\n{-# LANGUAGE ScopedTypeVariables #-}\n{-# LANGUAGE TypeOperators #-}\n\nmodule Control.Monad.Eff.Examples.VerboseAddition where\n\nimport Control.Monad.Eff\nimport Control.Monad.Eff.Lift\nimport Prelude hiding (log)\n\ndata Log v where\n  Log :: String -\u003e Log ()\n\nlog :: Member Log r =\u003e String -\u003e Eff r ()\nlog = send . Log\n\nrunLogger :: Eff (Log ': r) a -\u003e Eff r (a, [String])\nrunLogger = handleRelay ret handle\n  where\n    ret :: a -\u003e Eff r (a, [String])\n    ret x = return (x, [])\n    handle :: Handler Log r (a, [String])\n    handle (Log s) k = do\n      (x, ss) \u003c- k ()\n      return (x, s:ss)\n\nrunIOLogger :: forall r a. MemberU2 Lift (Lift IO) r =\u003e Eff (Log ': r) a -\u003e Eff r a\nrunIOLogger = handleRelay ret handle\n  where\n    ret :: a -\u003e Eff r a\n    ret = return\n    handle :: Handler Log r a\n    handle (Log s) k = lift (putStrLn s) \u003e\u003e= k\n\nexample :: Member Log r =\u003e Eff r Int\nexample = do\n  log \"I'm starting with 1...\"\n  let x = 1\n\n  log \"and I'm adding 2...\"\n  let y = 2\n\n  let r = x + y\n  log $ \"Looks like the result is \" ++ show r\n  return r\n```\nNow we can run the program in pure or impure way:\n\n```\nλ\u003e run (runLogger verboseAddition)\n(3,[\"I'm starting with 1...\",\"and I'm adding 2...\",\"Looks like the result is 3\"])\nλ\u003e runLift (runIOLogger verboseAddition)\nI'm starting with 1...\nand I'm adding 2...\nLooks like the result is 3\n3\n```\n\n### Teletype ([Purify code using free monads](http://www.haskellforall.com/2012/07/purify-code-using-free-monads.html))\n\n[Teletype.hs](src/Control/Monad/Eff/Examples/Teletype.hs)\n\n```haskell\n{-# LANGUAGE DataKinds #-}\n{-# LANGUAGE FlexibleContexts #-}\n{-# LANGUAGE GADTs #-}\n{-# LANGUAGE ScopedTypeVariables #-}\n{-# LANGUAGE TypeOperators #-}\n\nmodule Control.Monad.Eff.Examples.Teletype where\n\nimport Control.Monad.Eff\nimport Control.Monad.Eff.Lift\nimport System.Exit (exitSuccess)\n\ndata Teletype x where\n  GetLine     :: Teletype String\n  PutStrLn    :: String -\u003e Teletype ()\n  ExitSuccess :: Teletype ()\n\nputStrLn' :: Member Teletype r =\u003e String -\u003e Eff r ()\nputStrLn' = send . PutStrLn\n\ngetLine' :: Member Teletype r =\u003e Eff r String\ngetLine' = send GetLine\n\nexitSuccess' :: Member Teletype r =\u003e Eff r ()\nexitSuccess' = send ExitSuccess\n\nrunTeletype :: [String] -\u003e Eff (Teletype ': r) a -\u003e Eff r [String]\nrunTeletype ss = handleRelayS ss ret handle\n  where\n    ret :: [String] -\u003e a -\u003e Eff r [String]\n    ret _ a = return []\n\n    handle :: HandlerS [String] Teletype r [String]\n    handle (s:stdin) GetLine      k = k stdin s\n    handle _         GetLine      k = error \"Insufficient input\"\n    handle stdin     (PutStrLn s) k = do\n      stdout \u003c- k stdin ()\n      return (s:stdout)\n    handle _         ExitSuccess  k = return []\n\nrunIOTeletype :: forall r a. MemberU2 Lift (Lift IO) r =\u003e Eff (Teletype ': r) a -\u003e Eff r a\nrunIOTeletype = handleRelay ret handle\n  where\n    ret :: a -\u003e Eff r a\n    ret = return\n\n    handle :: Handler Teletype r a\n    handle GetLine      k = lift getLine      \u003e\u003e= k\n    handle (PutStrLn s) k = lift (putStrLn s) \u003e\u003e= k\n    handle ExitSuccess  k = lift exitSuccess  \u003e\u003e= k\n\nexample :: Member Teletype r =\u003e Eff r ()\nexample = do\n  str \u003c- getLine'\n  putStrLn' (\"put: \" ++ str)\n  str \u003c- getLine'\n  putStrLn' (\"put: \" ++ str)\n  exitSuccess'\n  putStrLn' \"should not appear\"\n```\n\nRun it purely:\n\n```\nλ\u003e run $ runTeletype [\"hello\", \"world\", \"and more\"] example\n[\"put: hello\",\"put: world\"]\n\nλ\u003e run $ runTeletype [\"hello\"] example\n*** Exception: Insufficient input\nCallStack (from HasCallStack):\n  error, called at /work/src/Control/Monad/Eff/Examples/Teletype.hs:35:39 in main:Control.Monad.Eff.Examples.Teletype\n```\n\nRun it in IO:\n\n```\nλ\u003e runLift $ runIOTeletype example\nhello\nput: hello\nworld\nput: world\n*** Exception: ExitSuccess\n```\n\n## Usage Tips\n\n### Effect Intepreter\n\nThe most complex part of new effect definition is the 'runX' function. As you can see in the above examples, it's usually defined by `handleRelay ret handle` with your customized `ret` to return value and `handle` to handle continuation.\n\nIt's similar to what you do to implement an instance of Monad (`ret` for `return`, `handle` for `\u003e\u003e=`). You can read `handleRelay ret handle` as run this monad with instance defined by `ret` and `handle`.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqzchenwl%2Fmore-extensible-effects","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqzchenwl%2Fmore-extensible-effects","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqzchenwl%2Fmore-extensible-effects/lists"}