What ?
Functionnal Tool: (de)composition with type checking, multiple dispatch.
Why ?
There is a lot of functionnal tools in python: toolz, funcy, fn.py, Pymonads…
They are great but none of them do type checking which, in my opinion, is a big loss from functionnal paradigm view.
How ?
With a complex and unmaintenable api: a single decorator called “arrow”.
For a technical explanation of arrows you can look at: Understanding arrows
For a theorical explanation: “Conceptual Mathematics: A First Introduction to Categories” [F. William Lawvere, Stephen H. Schanuel] is good start.
Prerequisites
- I strongly recommand to have at least notions about:
lambda calculi (Currying)
tuples in algebra notations (e.g. signatures)
basics of graph theory (related to category theory - morphisms, domain, codomain…)
Table of Contents
Installation
Install it from pypi:
pip install lawvere
or from sources:
git clone git@github.com:apieum/lawvere.git cd lawvere python setup.py install
Usage
Quick start:
for impatients
- arrow take only two optionnal arguments:
the domain or source: it’s a tuple of types (or just a type in case of 1-tuple)
the codomain or target: (a single type)
It returns a function decorator.
Syntax is compatible for python 2.7 to 3.x.. In case you target 3.x versions only you can use annotations instead of “arrow” arguments. “arrow” arguments override annotations.
Code compatible with 2.x and 3.x python versions.
from lawvere import arrow # obviously you don't need to use named parameters # it's just to illustrate their meaning. @arrow(domain=(int, int), codomain=int) def add(x, y): return x + y # currying: add2 = add(2) assert add2(3) == 5 assert add(1)(2) == 3 # successive calls # composition (pipe): # pass the result of first function to second add4 = add2 >> add2 assert add4(1) == 5 # composition (circle): # pass result of add(1) into add4 add5 = add4 << add(1) assert add5(5) == 10 # composed functions are tuples: assert isinstance(add4, tuple) assert add5[1] == add4 # equality is checked over signatures: assert add(1) == add(1) assert add5 == add(1) >> add2 >> add2 # operator precedence: assert add5 == add(1) >> (add2 << add2) assert add5 == add2 >> add2 << add(1) # Type Checking: type_checked = False try: add('a', 'b') == 'ab' except TypeError: type_checked = True assert type_checked, 'add should not exists for str types' # dispatch register # concat inherit arrow properties @add.register((str, str), str) def concat(x, y): return "%s %s" %(x, y) # can still call/curry... concat assert concat('a')('b') == 'a b' # add with str call concat assert add('a') == concat('a') # Concat is only defined for str type_checked = False try: assert concat(1, 2) == 3 except TypeError: type_checked = True assert type_checked, 'concat should not exists for int types' # Type Checking when composing: try: add >> concat(y='b') except TypeError as exc: message = str(exc) # hope message is clear :) assert message == "Cannot compose add -> int with concat(x:str=Undefined, y:str=b)" # if composition was circle message would be: # ... concat -> str with add(x:int=Undefined, y:int=Undefined)
Code compatible with 3.x versions only.
from lawvere import arrow @arrow def add(x:int, y:int) -> int: return x + y # identical use as Python 2 # # Type Checking: type_checked = False try: add('a', 'b') == 'ab' except TypeError: type_checked = True assert type_checked, 'add should not exists for str types' # dispatch @add.register def concat(x:str, y:str) -> str: return "%s %s" %(x, y) assert concat('a', 'b') == 'a b' # now add exists for str types: assert add('a', 'b') == 'a b' # type is also checked when composing... see python 2 ex.
Development
Your feedback, code review, improvements or bugs, and help to document is appreciated. You can contact me by mail: apieum [at] gmail [dot] com
Test recommended requirements:
pip install -r dev-requirements.txt
Sometimes –spec-color doesn’t function. Uninstall nosespec and nosecolor then reinstall nosecolor and nosespec separatly in this order (nosecolor first).
Launch tests:
git clone git@github.com:apieum/lawvere.git cd lawvere nosetests --with-spec --spec-color ./lawvere # or with watch # nosetests --with-spec --spec-color --with-watch ./lawvere
Metadata
Release files for lawvere 0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| lawvere-0.1.tar.gz | 16.5 kB | Without provenance | Details |
Release files / lawvere-0.1.tar.gz
| Download URL | lawvere-0.1.tar.gz |
|---|---|
| Size | 16.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e09036d8599c88bfe2dcecb08ca72c65c8f5fd0e72343c4cba837ed818ce19de
|
|
BLAKE2b-256 checksum How to use checksums |
d1c8eb7955963853d6f937494801cc29e2b82d1ebd22dc32f18d3ed59166b5de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
Security for lawvere 0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.