{"id":19845859,"url":"https://github.com/jluttine/haskpy","last_synced_at":"2026-03-10T18:03:09.007Z","repository":{"id":90800209,"uuid":"214691885","full_name":"jluttine/haskpy","owner":"jluttine","description":"Haskell/Hask inspired classes and functions in Python","archived":false,"fork":false,"pushed_at":"2024-09-04T08:13:53.000Z","size":1091,"stargazers_count":6,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-22T04:23:35.964Z","etag":null,"topics":["functional-programming","functor","haskell","monad","profunctor-optics","python"],"latest_commit_sha":null,"homepage":"https://haskpy.jaakkoluttinen.fi/","language":"Python","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/jluttine.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.rst","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}},"created_at":"2019-10-12T18:00:44.000Z","updated_at":"2025-04-28T12:25:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"6cdc7452-972b-4430-8c0e-9ec2c8cee320","html_url":"https://github.com/jluttine/haskpy","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/jluttine/haskpy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jluttine%2Fhaskpy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jluttine%2Fhaskpy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jluttine%2Fhaskpy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jluttine%2Fhaskpy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jluttine","download_url":"https://codeload.github.com/jluttine/haskpy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jluttine%2Fhaskpy/sbom","scorecard":{"id":523709,"data":{"date":"2025-08-11","repo":{"name":"github.com/jluttine/haskpy","commit":"c9566dcba34300b2c5edca2fee354da5a8126bfd"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.8,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/doc.yml:1","Warn: no topLevel permission defined: .github/workflows/pypi.yml:1","Warn: no topLevel permission defined: .github/workflows/test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/doc.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/jluttine/haskpy/doc.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/doc.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/jluttine/haskpy/doc.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/doc.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/jluttine/haskpy/doc.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pypi.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/jluttine/haskpy/pypi.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pypi.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/jluttine/haskpy/pypi.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/jluttine/haskpy/test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/jluttine/haskpy/test.yml/master?enable=pin","Warn: pipCommand not pinned by hash: .github/workflows/doc.yml:25","Warn: pipCommand not pinned by hash: .github/workflows/pypi.yml:20","Warn: pipCommand not pinned by hash: .github/workflows/pypi.yml:21","Warn: pipCommand not pinned by hash: .github/workflows/test.yml:23","Info:   0 out of   7 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   4 pipCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-20T03:46:04.790Z","repository_id":90800209,"created_at":"2025-08-20T03:46:04.790Z","updated_at":"2025-08-20T03:46:04.790Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30346482,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-10T15:55:29.454Z","status":"ssl_error","status_checked_at":"2026-03-10T15:54:58.440Z","response_time":106,"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":["functional-programming","functor","haskell","monad","profunctor-optics","python"],"created_at":"2024-11-12T13:09:31.872Z","updated_at":"2026-03-10T18:03:08.997Z","avatar_url":"https://github.com/jluttine.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# HaskPy - Haskell types and functions in Python\n\nHask is the category of types and functions in Haskell. This package provides\nclasses and functions inspired by Hask.\n\nDocumentation is available at [jluttine.github.io/haskpy](https://jluttine.github.io/haskpy/).\n\n[![Test status](https://github.com/jluttine/haskpy/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/jluttine/haskpy/actions/workflows/test.yml)\n[![Release](https://img.shields.io/pypi/v/haskpy.svg)](https://pypi.org/project/haskpy/)\n\n\n## Overview\n\n### Features\n\n- Typeclasses: `Functor`, `Applicative`, `Monad`, `Semigroup`, `Monoid`,\n  `Commutative`, `Foldable`, `Contravariant`, `Profunctor`, `Cartesian`,\n  `Cocartesian`\n\n  - **TODO:** `Traversable`, `Bifunctor`, `Monoidal`, `Ord`, `Show`, `Read`\n\n- Types and type constructors: `Identity`, `Maybe`, `Either`, `List`,\n  `Function`, `Compose`, `LinkedList`\n\n  - **TODO:** `Constant`, `Validation`, `Dictionary`, `State`, `Reader`,\n    `Writer`, `IO`\n\n- Monad transformers: `MaybeT`, `IdentityT`\n\n  - **TODO:** `StateT`, `ReaderT`, `WriterT`, `ListT`\n\n- Simple monoids: `Sum`, `All`, `Any`, `String`, `Endo`\n\n  - **TODO:** `Product`\n\n- Profunctor optics: `adapter`, `lens`, `prism`\n\n  - **TODO**: `traversal`, `grate`, `affine`, `setter`\n\n- Operators for common tasks: ``**`` for function composition or functorial\n  mapping, ``@`` for applicative application, ``%`` for monadic binding and\n  ``\u003e\u003e`` for applicative/monadic sequencing.\n\n- Property-based testing of typeclass laws\n\nHaskPy has implemented typeclass laws as property-based tests. Thus, one can\neasily test that an implementation satisfies all the laws it should. Just add\nsomething like this to your test module and run with pytest:\n\n```python\nfrom haskpy.utils import make_test_class\nfrom mystuff import MyClass\nTestMyClass = make_test_class(MyClass)\n```\n\nThis will automatically verify that `MyClass` satisfies all the typeclass laws\nof those typeclasses that it inherits. It makes use of great [Hypothesis\npackage](https://hypothesis.readthedocs.io/en/latest/).\n\n\n## Examples\n\n### Functors\n\nA minimal example of functorial mapping:\n\n```python\n\u003e\u003e\u003e from haskpy import map, List\n\u003e\u003e\u003e map(lambda x: x**2, List(1, 2, 3, 4, 5))\nList(1, 4, 9, 16, 25)\n```\n\nLift over two layers of functorial structure:\n\n```python\n\u003e\u003e\u003e from haskpy import map, List, Just, Nothing\n\u003e\u003e\u003e map(map(lambda x: x**2))(List(Just(1), Nothing, Just(3), Just(4), Nothing))\nList(Just(1), Nothing, Just(9), Just(16), Nothing)\n```\n\nNote that `haskpy.map` works for all Functor instances. That is, you don't need\nto use a different function to lift over different functors. You can even create\nfunction that performs some operation to values contained in any two-layer\nfunctorial structure. In the following example, `square` squares the values\ninside a two-layer functor:\n\n```python\n\u003e\u003e\u003e square = map(map(lambda x: x**2))\n\u003e\u003e\u003e square(List(Just(1), Nothing, Just(3)))\nList(Just(1), Nothing, Just(9))\n\u003e\u003e\u003e square(List(List(1, 2, 3), List(4, 5)))\nList(List(1, 4, 9), List(16, 25))\n```\n\nEven functions are functors if they have been decorated with `function`:\n\n```python\n\u003e\u003e\u003e @function\n... def f(x):\n...     return List(x, 2*x, 3*x)\n\u003e\u003e\u003e square(f)(3)\nList(9, 36, 81)\n```\n\n### Functions\n\nHaskPy provides two different function types:\n\n- `Function` for curried functions that only accept fixed number of positional\n  arguments\n- `Uncurried` for uncurried functions that are similar to Python functions in\n  that they accept both positional and keyword arguments, and\n  \nBoth of these types have, for instance, `Functor` and `Monad` implementations.\n  \n#### Curried functions\n\n`Function` objects are curried and they should only take a fixed number of\npositional arguments (at least one argument and no optional arguments).\n\nDecorator `function` just converts a normal Python function into a curried\n`Function` object:\n\n```python\n\u003e\u003e\u003e from haskpy import function\n\u003e\u003e\u003e @function\n... def concat(x, y, z):\n...    return x + y + z\n\u003e\u003e\u003e concat(\"a\")(\"b\")(\"c\")\n\"abc\"\n\u003e\u003e\u003e concat(\"a\")(\"b\", \"c\")\n\"abc\"\n```\n\nNote that it doesn't convert the function into nested one-argument functions (as\ncurrying strictly speaking should do) but it gives more flexibility by accepting\nany number of arguments (even no arguments at all), but the number of arguments\nmust be fixed. Keyword arguments aren't supported because of consistency.\n\nNote also that nested functions can be treated as one multi-argument function:\n\n``` python\n\u003e\u003e\u003e @function\n... def prepend(x):\n...     @function\n...     def _run(y):\n...         return x + y\n...     return _run\n\u003e\u003e\u003e prepend(\"foo\", \"bar\")\n\"foobar\"\n```\n\nSo, in terms of usability, a function with multiple arguments is identical to\nnested functions. However, they might have performance differences - even\nsignificant ones.\n\nAn example of lifting a multi-argument function over `Maybe`:\n\n``` python\n\u003e\u003e\u003e concat ** Just(\"a\") @ Just(\"b\") @ Just(\"c\")\nJust(\"abc\")\n```\n\nIn terms of types, a function of multiple arguments is interpreted as a function\nthat takes only one argument and returns another function. This is important to\nunderstand, for instance, when using operations such as functorial `map`: they\nmodify what the function returns after receiving the first argument - which in\ncase of a \"multi-argument\" function is a \"partially applied\" function.\n\nAlmost all functions in HaskPy have been decorated with `function`. It is\npossible to use it to decorate methods too.\n\n\n#### Uncurried (Python-like) functions\n\n`Uncurried` objects represent uncurried functions, which are probably more\nfamiliar to regular Python users. Uncurried functions don't support partial\napplication: you pass all the arguments in a single function call and the\nfunction gets evaluated. That's it. Operations such as functorial `map` affect\nthe value the function returns given all the arguments:\n\n```python\n\u003e\u003e\u003e from haskpy import uncurried\n\u003e\u003e\u003e @uncurried\n... def line(x, k, b=0):\n...     return k * x + b\n\u003e\u003e\u003e map(lambda y: -y)(line)(5, k=2, b=100)\n-110\n```\n\nIf you don't pass all the arguments, you'll get an error:\n\n``` python\n\u003e\u003e\u003e map(lambda y: -y)(line)(5)\nTypeError: line() missing 1 required positional argument: 'k'\n```\n\nThis is very different from how `Function` works. In a way, this uncurried\nfunction type is like a function that takes only one argument but this single\nargument is a collection of positional and keyword arguments. The positional\narguments can be seen as a list (that can contain different types) and the\nkeyword arguments can be seen as a dictionary. So, the single argument is a\ntuple of a list and a dictionary. Interestingly, in Python syntax, the function\narguments are wrapped in parentheses similarly as in tuples.\n\n`Uncurried` might be preferred over `Function`, for instance, when you need\nkeyword arguments or varying number of positional arguments, or when operations\nsuch as `map` should be applied only after all the arguments have been given.\n\n## Copyright\n\nCopyright (C) 2019-2021 Jaakko Luttinen\n\nHaskPy is licensed under the MIT License. See LICENSE file for a text of the\nlicense or visit http://opensource.org/licenses/MIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjluttine%2Fhaskpy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjluttine%2Fhaskpy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjluttine%2Fhaskpy/lists"}