{"id":29302840,"url":"https://github.com/bowenfu/hspp","last_synced_at":"2025-10-19T14:21:12.560Z","repository":{"id":37003931,"uuid":"497247456","full_name":"BowenFu/hspp","owner":"BowenFu","description":"hspp: An experimental library to bring Haskell Style Programming to C++.","archived":false,"fork":false,"pushed_at":"2022-10-24T00:02:11.000Z","size":449,"stargazers_count":155,"open_issues_count":4,"forks_count":0,"subscribers_count":7,"default_branch":"main","last_synced_at":"2024-05-01T16:28:00.316Z","etag":null,"topics":["concurrency","concurrent-programming","cpp","cpp-library","cpp17","cpp20","do-notation","functional-programming","haskell","monad","monadic","monadic-interface","parser-combinator","parser-combinators","software-transactional-memory","stm","transactional-memory"],"latest_commit_sha":null,"homepage":"https://bowenfu.github.io/hspp","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BowenFu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["bowenfu"],"patreon":"bfu","open_collective":null,"ko_fi":"bowenfu","tidelift":null,"community_bridge":null,"liberapay":"bfu","issuehunt":null,"otechie":null,"custom":"https://afdian.net/a/amazing42"}},"created_at":"2022-05-28T07:34:12.000Z","updated_at":"2024-04-30T08:15:22.000Z","dependencies_parsed_at":"2022-07-07T16:27:25.745Z","dependency_job_id":null,"html_url":"https://github.com/BowenFu/hspp","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/BowenFu/hspp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BowenFu%2Fhspp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BowenFu%2Fhspp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BowenFu%2Fhspp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BowenFu%2Fhspp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BowenFu","download_url":"https://codeload.github.com/BowenFu/hspp/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BowenFu%2Fhspp/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263991513,"owners_count":23540669,"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":["concurrency","concurrent-programming","cpp","cpp-library","cpp17","cpp20","do-notation","functional-programming","haskell","monad","monadic","monadic-interface","parser-combinator","parser-combinators","software-transactional-memory","stm","transactional-memory"],"created_at":"2025-07-07T00:14:03.803Z","updated_at":"2025-10-19T14:21:07.488Z","avatar_url":"https://github.com/BowenFu.png","language":"C++","readme":"# hspp\n\n![hspp](./hspp.svg)\n\n## hspp: bring Haskell Style Programming to C++.\n\n![Standard](https://img.shields.io/badge/c%2B%2B-17/20-blue.svg)\n\n![Platform](https://img.shields.io/badge/platform-linux-blue)\n![Platform](https://img.shields.io/badge/platform-osx-blue)\n![Platform](https://img.shields.io/badge/platform-win-blue)\n\n[![CMake](https://github.com/BowenFu/hspp/actions/workflows/cmake.yml/badge.svg)](https://github.com/BowenFu/hspp/actions/workflows/cmake.yml)\n[![CMake](https://github.com/BowenFu/hspp/actions/workflows/sanitizers.yml/badge.svg)](https://github.com/BowenFu/hspp/actions/workflows/sanitizers.yml)\n![GitHub license](https://img.shields.io/github/license/BowenFu/hspp.svg)\n[![codecov](https://codecov.io/gh/BowenFu/hspp/branch/main/graph/badge.svg)](https://codecov.io/gh/BowenFu/hspp)\n\nC++ are introducing monadic interfaces. We have compared hspp and some proposals, refer to [std::optional and std::expected](https://github.com/BowenFu/hspp/blob/main/sample/proposal.cpp) for more details.\n\n\n## Mom, can we have monadic do notation / monad comprehension in C++?\n\nHere you are!\n\n[badge.godbolt]: https://img.shields.io/badge/try-godbolt-blue\n\n### Sample 1 for Maybe (similar to std::optional) Monad used in do notation\n\nThe sample is originated from Learn You a Haskell for Great Good!\n\n\"Pierre has decided to take a break from his job at the fish farm and try tightrope walking. He's not that bad at it, but he does have one problem: birds keep landing on his balancing pole!\nLet's say that he keeps his balance if the number of birds on the left side of the pole and on the right side of the pole is within three.\"\n\nNote that Pierre may also suddenly slip and fall when there is a banana.\n\nOriginal Haskell version\n\n```haskell\nroutine :: Maybe Pole\nroutine = do\n    start \u003c- return (0,0)\n    first \u003c- landLeft 2 start\n    Nothing\n    second \u003c- landRight 2 first\n    landLeft 1 second\n```\n\nC++ version using hspp\n\n[godbolt3]: https://godbolt.org/z/9T5sa64nE\n\n[![Try it on godbolt][badge.godbolt]][godbolt3]\n\n```c++\nId\u003cPole\u003e start, first, second;\nauto const routine = do_(\n    start \u003c= return_ | Pole{0,0}),\n    first \u003c= (landLeft | 2 | start),\n    nothing\u003cPole\u003e,\n    second \u003c= (landRight | 2 | first),\n    landLeft | 1 | second\n);\n```\n\n### Sample 2 for Either (similar to std::expected) Monad used in do notation\n\nIn [p0323r0](https://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0323r0.pdf), there is a proposal to add a utility class to represent expected monad.\n\nA use case would be like\n```c++\nexpected\u003cint, error_condition\u003e safe_divide(int i, int j)\n{\n    if (j == 0) return make_unexpected(arithmetic_errc::divide_by_zero);\n    if (i%j != 0) return make_unexpected(arithmetic_errc::not_integer_division);\n    else return i / j;\n}\n\n// i / k + j / k\nexpected\u003cint, error_condition\u003e f(int i, int j, int k)\n{\n    return safe_divide(i, k).bind([=](int q1)\n    {\n        return safe_divide(j,k).bind([=](int q2)\n        {\n            return q1+q2;\n        });\n    });\n}\n```\n\nIn hspp, the codes would be like\n```c++\nauto safe_divide(int i, int j) -\u003e Either\u003carithmetic_errc, int\u003e\n{\n    if (j == 0) return toLeft | arithmetic_errc::divide_by_zero;\n    if (i%j != 0) return toLeft | arithmetic_errc::not_integer_division;\n    else return toRight || i / j;\n}\n\nauto f(int i, int j, int k) -\u003e Either\u003carithmetic_errc, int\u003e\n{\n    Id\u003cint\u003e q1, q2;\n    return do_(\n        q1 \u003c= safe_divide(i, k),\n        q2 \u003c= safe_divide(j, k),\n        return_ || q1 + q2\n    );\n}\n```\n\nRefer to [proposal sample](https://github.com/BowenFu/hspp/blob/main/sample/proposal.cpp) for complete sample codes.\n\n### Sample 3 for monadic do notation for a vector monad\n\nFilter even numbers.\n\n[godbolt1]: https://godbolt.org/z/MM7MW9MPY\n\n[![Try it on godbolt][badge.godbolt]][godbolt1]\n\n```c++\n    using namespace hspp;\n    using namespace hspp::doN;\n    Id\u003cint\u003e i;\n    auto const result = do_(\n        i \u003c= std::vector{1, 2, 3, 4},\n        guard | (i % 2 == 0),\n        return_ | i\n    );\n```\n\n### Sample 4 for monad comprehension for a range monad\n\nObtain an infinite range of Pythagorean triples.\n\nHaskell version\n\n```haskell\ntriangles = [(i, j, k) | k \u003c- [1..], i \u003c- [1..k], j \u003c- [i..k] , i^2 + j^2 == k^2]\n```\n\n[godbolt2]: https://godbolt.org/z/o7qj6o111\n\n[![Try it on godbolt][badge.godbolt]][godbolt2]\n\n```c++\n    using namespace hspp::doN;\n    using namespace hspp::data;\n    Id\u003cint\u003e i, j, k;\n    auto const rng = _(\n        makeTuple\u003c3\u003e | i | j | k,\n        k \u003c= (enumFrom | 1),\n        i \u003c= (within | 1 | k),\n        j \u003c= (within | i | k),\n        if_ || (i*i + j*j == k*k)\n    );\n```\n\nEquivalent version using RangeV3 would be [link](http://ericniebler.com/2014/04/27/range-comprehensions/)\n\n```C++\nusing namespace ranges;\n\n// Lazy ranges for generating integer sequences\nauto const intsFrom = view::iota;\nauto const ints = [=](int i, int j)\n    {\n        return view::take(intsFrom(i), j-i+1);\n    };\n\n// Define an infinite range of all the Pythagorean\n// triples:\nauto triples =\n    view::for_each(intsFrom(1), [](int z)\n    {\n        return view::for_each(ints(1, z), [=](int x)\n        {\n            return view::for_each(ints(x, z), [=](int y)\n            {\n                return yield_if(x*x + y*y == z*z,\n                    std::make_tuple(x, y, z));\n            });\n        });\n    });\n```\n\n\n### Sample 5 for Function Monad used in do notation\n\nWe have two functions, plus1, and showStr. With do notation we construct a new function that will accept an integer as argument and return a tuple of results of the two functions.\n\n[godbolt4]: https://godbolt.org/z/d58Ezbxjz\n\n[![Try it on godbolt][badge.godbolt]][godbolt4]\n\n```c++\n    auto plus1 = toFunc\u003c\u003e | [](int x){ return 1+x; };\n    auto showStr = toFunc\u003c\u003e | [](int x){ return show | x; };\n\n    Id\u003cint\u003e x;\n    Id\u003cstd::string\u003e y;\n    auto go = do_(\n        x \u003c= plus1,\n        y \u003c= showStr,\n        return_ || makeTuple\u003c2\u003e | x | y\n    );\n    auto result = go | 3;\n    std::cout \u003c\u003c std::get\u003c0\u003e(result) \u003c\u003c std::endl;\n    std::cout \u003c\u003c std::get\u003c1\u003e(result) \u003c\u003c std::endl;\n```\n\n### Sample 6 for parser combinator\n\nOriginal haskell version [Monadic Parsing in Haskell](https://www.cambridge.org/core/journals/journal-of-functional-programming/article/monadic-parsing-in-haskell/E557DFCCE00E0D4B6ED02F3FB0466093)\n\n```haskell\nexpr, term, factor, digit :: Parser Int\n\ndigit  = do {x \u003c- token (sat isDigit); return (ord x - ord '0')}\n\nfactor = digit +++ do {symb \"(\"; n \u003c- expr; symbol \")\"; return n}\n\nterm   = factor `chainl1` mulop\n\nexpr   = term   `chainl1` addop\n```\n\nC++ version\n[parse_expr](https://github.com/BowenFu/hspp/blob/main/sample/parse_expr.cpp)\n\n[godbolt5]: https://godbolt.org/z/r7WTYjGYa\n\n[![Try it on godbolt][badge.godbolt]][godbolt5]\n\n```c++\nId\u003cchar\u003e x;\nauto const digit = do_(\n    x \u003c= (token || sat | isDigit),\n    return_ | (x - '0')\n);\n\nextern TEParser\u003cint\u003e const expr;\n\nId\u003cint\u003e n;\nauto const factor =\n    digit \u003calt\u003e\n        do_(\n            symb | \"(\"s,\n            n \u003c= expr,\n            symb | \")\"s,\n            return_ | n\n        );\n\nauto const term = factor \u003cchainl1\u003e mulOp;\n\nTEParser\u003cint\u003e const expr = toTEParser || (term \u003cchainl1\u003e addOp);\n```\n\n### Sample 7 for STM / concurrent\n\n[concurrent.cpp](https://github.com/BowenFu/hspp/blob/main/test/hspp/concurrent.cpp)\n\n[godbolt6]: https://godbolt.org/z/zj9Mc1h4h\n\n[![Try it on godbolt][badge.godbolt]][godbolt6]\n\n\nTransfer from one account to another one atomically.\n```c++\nId\u003cAccount\u003e from, to;\nId\u003cInteger\u003e v1, v2;\nauto io_ = do_(\n    from \u003c= (newTVarIO | Integer{200}),\n    to   \u003c= (newTVarIO | Integer{100}),\n    transfer | from | to | 50,\n    v1 \u003c= (showAccount | from),\n    v2 \u003c= (showAccount | to),\n    hassert | (v1 == 150) | \"v1 should be 150\",\n    hassert | (v2 == 150) | \"v2 should be 150\"\n);\nio_.run();\n```\n\nWithdraw from an account but waiting for sufficient money.\n```c++\nId\u003cAccount\u003e acc;\nauto io_ = do_(\n    acc \u003c= (newTVarIO | Integer{100}),\n    forkIO | (delayDeposit | acc | 1),\n    putStr | \"Trying to withdraw money...\\n\",\n    atomically | (limitedWithdrawSTM | acc | 101),\n    putStr | \"Successful withdrawal!\\n\"\n);\n\nio_.run();\n```\n\nAnd we can also compose two STMs with `orElse`\n```c++\n// (limitedWithdraw2 acc1 acc2 amt) withdraws amt from acc1,\n// if acc1 has enough money, otherwise from acc2.\n// If neither has enough, it retries.\nconstexpr auto limitedWithdraw2 = toFunc\u003c\u003e | [](Account acc1, Account acc2, Integer amt)\n{\n    return orElse | (limitedWithdrawSTM | acc1 | amt) | (limitedWithdrawSTM | acc2 | amt);\n};\n\nId\u003cAccount\u003e acc1, acc2;\nauto io_ = do_(\n    acc1 \u003c= (atomically | (newTVar | Integer{100})),\n    acc2 \u003c= (atomically | (newTVar | Integer{100})),\n    showAcc | \"Left pocket\" | acc1,\n    showAcc | \"Right pocket\" | acc2,\n    forkIO | (delayDeposit | acc2 | 1),\n    print | \"Withdrawing $101 from either pocket...\",\n    atomically | (limitedWithdraw2 | acc1 | acc2 | Integer{101}),\n    print | \"Successful withdrawal!\",\n    showAcc | \"Left pocket\" | acc1,\n    showAcc | \"Right pocket\" | acc2\n);\n\nio_.run();\n```\n\n### Sample 8 for coroutine\n\n[coroutine](https://github.com/BowenFu/hspp/blob/main/sample/coroutine.cpp)\n\n```c++\nusing O = std::string;\nusing I = std::string;\n\n// coroutine for handling strings\nId\u003cstd::string\u003e name, color;\nauto const example1 = do_(\n    name \u003c= (yield\u003cIO, O, I, std::string\u003e | \"What's your name? \"),\n    lift\u003cProducing, O, I\u003e || putStrLn | (\"Hello, \" + name),\n    color \u003c= (yield\u003cIO, O, I, std::string\u003e | \"What's your favorite color? \"),\n    lift\u003cProducing, O, I\u003e || putStrLn | (\"I like \" + color + \", too.\")\n);\n\nauto const foreverKResult = foreverK | [](std::string str) -\u003e Producing\u003cIO, O, I, std::string\u003e\n{\n    return do_(\n        lift\u003cProducing, O, I\u003e || putStrLn | str,\n        (lift\u003cProducing, O, I\u003e | getLine) \u003e\u003e= yield\u003cIO, O, I, std::string\u003e\n    );\n};\n\n// coroutine for handling io\nauto const stdOutIn = toConsumingPtr_\u003cIO, O, I, _O_\u003e || foreverKResult;\n\n// let the two coroutines hand over control to each other by turn.\nauto io_ = example1 \u003cSS\u003e stdOutIn;\nio_.run();\n\n```\n\nThe sample running would be like\n```\n\u003e What's your name?\n\u003c Bob\n\u003e Hello, Bob\n\u003e What's your favorite color?\n\u003c Red\n\u003e I like Red, too.\n```\n\n## Haskell vs Hspp (Incomplete list)\n\n| Haskell       | Hspp |\n| -------       | ---- |\n| function      | Function / GenericFunction |\n| f x y         | f \\| x \\| y |\n| f $ g x       | f \\|\\| g \\| x|\n| f . g $ x     | f \\\u003co\\\u003e g \\|\\| x|\n| a \\`f\\` b     | a \\\u003cf\\\u003e b |\n|[f x \\| x \u003c- xs, p x]| \\_(f \\| x, x \u003c= xs, if\\_ \\|\\| p \\| x) |\n| list (lazy)   | range |\n| list (strict) | std::vector/list/forward_list|\n| do {patA \u003c- action1; action2} | do_(patA \u003c= action1, action2) |\n| f \u003c$\u003e v       | f \\\u003cfmap\\\u003e v |\n| f \u003c*\u003e v       | f \\\u003cap\\\u003e v |\n| pure a        | pure \\| a |\n| m1 \u003e\u003e m2      | m1 \u003e\u003e m2 |\n| m1 \u003e\u003e= f      | m1 \u003e\u003e= f |\n| return a      | return_ \\| a |\n\n\n## Why bother?\n\nThe library is\n\n1. for fun,\n2. to explore the interesting features of Haskell,\n3. to explore the boundary of C++,\n4. to facilitate the translation of some interesting Haskell codes to C++.\n\nThis library is still in active development and not production ready.\n\nDiscussions / issues / PRs are welcome.\n\n## Related\n\nHaskell pattern matching is not covered in this repo. You may be interested in [match(it)](https://github.com/BowenFu/matchit.cpp) if you want to see how pattern matching works in C++.\n\n## Support this project\n\nPlease star the repo, share the repo, or [sponsor $1](https://github.com/sponsors/BowenFu) to let me know this project matters.\n\n## Star History\n\n[![Star History Chart](https://api.star-history.com/svg?repos=bowenfu/hspp\u0026type=Date)](https://star-history.com/#bowenfu/hspp\u0026Date)\n","funding_links":["https://github.com/sponsors/bowenfu","https://patreon.com/bfu","https://ko-fi.com/bowenfu","https://liberapay.com/bfu","https://afdian.net/a/amazing42","https://github.com/sponsors/BowenFu"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbowenfu%2Fhspp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbowenfu%2Fhspp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbowenfu%2Fhspp/lists"}