https://pypi.org/project/koda/ Skip to main content Switch to mobile version Warning Some features may not work without JavaScript. Please try enabling it if you encounter problems. PyPI Search PyPI [ ] Search * Help * Sponsors * Log in * Register Menu * Help * Sponsors * Log in * Register Search PyPI [ ] Search koda 0.10.2 pip install koda Copy PIP instructions Latest version Released: Feb 6, 2022 Type-safe functional tools for Python Navigation * Project description * Release history * Download files Project links * Homepage Statistics GitHub statistics: * Stars: * Forks: * Open issues/PRs: View statistics for this project via Libraries.io, or by using our public dataset on Google BigQuery Meta License: MIT License (MIT) Author: Keith Philpott Tags functional programming, type hints Requires: Python >=3.8, <4.0 Maintainers Avatar for keithasaurus from gravatar.com keithasaurus Classifiers * License + OSI Approved :: MIT License * Programming Language + Python :: 3 + Python :: 3.8 + Python :: 3.9 + Python :: 3.10 * Project description * Project details * Release history * Download files Project description Koda Koda is a collection of practical type-safe tools for Python. At it's core are a number of datatypes that are common in functional programming. Maybe Maybe is similar to Python's Optional type. It has two variants: Nothing and Just, and they work in similar ways to what you may have seen in other languages. from koda import Maybe, Just, nothing a: Maybe[int] = Just(5) b: Maybe[int] = nothing To know if a Maybe is a Just or a Nothing, you'll need to inspect it. from koda import Just, Maybe maybe_str: Maybe[str] = function_returning_maybe_str() # unwrap by checking instance type if isinstance(maybe_str, Just): print(maybe_str.val) else: print("No value!") # unwrap with structural pattern matching (python 3.10 +) match maybe_str: case Just(val): print(val) case Nothing: print("No value!") Maybe has methods for conveniently stringing logic together. Maybe.map from koda import Just, nothing Just(5).map(lambda x: x + 10) # Just(15) nothing.map(lambda x: x + 10) # Nothing Just(5).map(lambda x: x + 10).map(lambda x: f"abc{x}") # Just("abc15") Maybe.flat_map from koda import Maybe, Just, nothing def safe_divide(dividend: int, divisor: int) -> Maybe[float]: if divisor != 0: return Just(dividend / divisor) else: return nothing Just(5).flat_map(lambda x: safe_divide(10, x)) # Just(2) Just(0).flat_map(lambda x: safe_divide(10, x)) # Nothing nothing.flat_map(lambda x: safe_divide(10, x)) # Nothing Result Result provides a means of representing whether a computation succeeded or failed. To represent success, we can use OK; for failures we can use Err. Compared to Maybe, Result is perhaps most useful in that the "failure" case also returns data, whereas Nothing contains no data. from koda import Ok, Err, Result def safe_divide_result(dividend: int, divisor: int) -> Result[float, str]: if divisor != 0: return Ok(dividend / divisor) else: return Err("cannot divide by zero!") Ok(5).flat_map(lambda x: safe_divide_result(10, x)) # Ok(2) Ok(0).flat_map(lambda x: safe_divide_result(10, x)) # Err("cannot divide by zero!") Err("some other error").map(lambda x: safe_divide_result(10, x)) # Err("some other error") Result can be convenient with try/except logic. from koda import Result, Ok, Err def divide_by(dividend: int, divisor: int) -> Result[float, ZeroDivisionError]: try: return Ok(dividend / divisor) except ZeroDivisionError as exc: return Err(exc) divided: Result[float, ZeroDivisionError] = divide_by(10, 0) # Err(ZeroDivisionError("division by zero")) Another way to perform the same computation would be to use safe_try: from koda import Result, safe_try # not safe on its own! def divide(dividend: int, divisor: int) -> float: return dividend / divisor # safe if used with `safe_try` divided_ok: Result[float, ZeroDivisionError] = safe_try(divide, 10, 2) # Ok(5) divided_err: Result[float, ZeroDivisionError] = safe_try(divide, 10, 0) # Err(ZeroDivisionError("division by zero")) More There are many other functions and datatypes included. Some examples: compose Combine functions by sequencing. from koda import compose from typing import Callable def int_to_str(val: int) -> str: return str(val) def prepend_str_abc(val: str) -> str: return f"abc{val}" combined_func: Callable[[int], str] = compose(int_to_str, prepend_str_abc) assert combined_func(10) == "abc10" mapping_get Try to get a value from a Mapping object, and return an unambiguous result. from koda import mapping_get, Just, Maybe, nothing example_dict: dict[str, Maybe[int]] = {"a": Just(1), "b": nothing} assert mapping_get(example_dict, "a") == Just(Just(1)) assert mapping_get(example_dict, "b") == Just(nothing) assert mapping_get(example_dict, "c") == nothing As a comparison, note that dict.get can return ambiguous results: from typing import Optional example_dict: dict[str, Optional[int]] = {"a": 1, "b": None} assert example_dict.get("b") is None assert example_dict.get("c") is None We can't tell from the resulting value whether the None was the value for a key, or whether the key was not present in the dict load_once Create a lazy function, which will only call the passed-in function the first time it is called. After it is called, the value is cached. The cached value is returned on each successive call. from random import random from koda import load_once call_random_once = load_once(random) # has not called random yet retrieved_val: float = call_random_once() assert retrieved_val == call_random_once() maybe_to_result Convert a Maybe to a Result type. from koda import maybe_to_result, Just, nothing, Ok, Err assert maybe_to_result("value if nothing", nothing) == Err("value if nothing") assert maybe_to_result("value if nothing", Just(5)) == Ok(5) result_to_maybe Convert a Result to a Maybe type. from koda import result_to_maybe, Just, nothing, Ok, Err assert result_to_maybe(Ok(5)) == Just(5) assert result_to_maybe(Err("any error")) == nothing Intent Koda is intended to focus on a small set of practical data types and utility functions for Python. It will not grow to encompass every possible functional or typesafe concept. Similarly, the intent of this library is to avoid requiring extra plugins (beyond a type-checker like mypy or pyright) or specific typchecker settings. As such, it is unlikely that things like Higher Kinded Types emulation or extended type inference will be implemented in this library. Project details Project links * Homepage Statistics GitHub statistics: * Stars: * Forks: * Open issues/PRs: View statistics for this project via Libraries.io, or by using our public dataset on Google BigQuery Meta License: MIT License (MIT) Author: Keith Philpott Tags functional programming, type hints Requires: Python >=3.8, <4.0 Maintainers Avatar for keithasaurus from gravatar.com keithasaurus Classifiers * License + OSI Approved :: MIT License * Programming Language + Python :: 3 + Python :: 3.8 + Python :: 3.9 + Python :: 3.10 Release history Release notifications | RSS feed This version [blue-cube] 0.10.2 Feb 6, 2022 [white-cube] 0.10.1 Feb 5, 2022 [white-cube] 0.10.0 Feb 5, 2022 [white-cube] 0.9.1 Feb 5, 2022 [white-cube] 0.9.0 Feb 5, 2022 Download files Download the file for your platform. If you're not sure which to choose, learn more about installing packages. Source Distribution koda-0.10.2.tar.gz (9.1 kB view hashes) Uploaded Feb 6, 2022 source Built Distribution koda-0.10.2-py3-none-any.whl (8.1 kB view hashes) Uploaded Feb 6, 2022 py3 Close Hashes for koda-0.10.2.tar.gz Hashes for koda-0.10.2.tar.gz Algorithm Hash digest SHA256 b15737ce660077a85a9c772f032a0c1170b3963d391c977a707b0deccb0fa11f Copy MD5 4e5f3d4eed50927f29389537f7a0d183 Copy BLAKE2-256 bf8e9ab5c9b1dc21fb3ddc3e2d82baa2b56e9ad7c81d581f3ee9c7af96e84432 Copy Close Close Hashes for koda-0.10.2-py3-none-any.whl Hashes for koda-0.10.2-py3-none-any.whl Algorithm Hash digest SHA256 3a2def13887e606b8dbb21a6c15d3facfddc8260cc918a6faefcc93441ccb24c Copy MD5 bf7e8ab6f50f55816110e35e5d048086 Copy BLAKE2-256 a74e993c4e5a11a2ab35342e41b02256b9a05e64109f91aeb98d047e8f277bae Copy Close [white-cube] Help * Installing packages * Uploading packages * User guide * FAQs About PyPI * PyPI on Twitter * Infrastructure dashboard * Package index name retention * Our sponsors Contributing to PyPI * Bugs and feedback * Contribute on GitHub * Translate PyPI * Development credits Using PyPI * Code of conduct * Report security issue * Privacy policy * Terms of use --------------------------------------------------------------------- Status: all systems operational Developed and maintained by the Python community, for the Python community. Donate today! (c) 2022 Python Software Foundation Site map Switch to desktop version * English * espanol * francais * Ri Ben Yu * portugues (Brasil) * ukrayins'ka * Ellenika * Deutsch * Zhong Wen (Jian Ti ) * Zhong Wen (Fan Ti ) * russkii * `bryt * esperanto Supported by AWS AWS Cloud computing Datadog Datadog Monitoring Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Huawei Huawei PSF Sponsor Microsoft Microsoft PSF Sponsor NVIDIA NVIDIA PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page