Skip to main content

Implicit lambdas with placeholder notation and code generation

Project description

# Implicit lambdas

[![Build Status](https://www.travis-ci.com/BlackHC/implicit_lambda.svg?branch=master)](https://www.travis-ci.com/BlackHC/implicit_lambda) [![codecov](https://codecov.io/gh/BlackHC/implicit_lambda/branch/master/graph/badge.svg)](https://codecov.io/gh/BlackHC/implicit_lambda) [![PyPI](https://img.shields.io/badge/PyPI-implicit_lambda-blue.svg)](https://pypi.python.org/pypi/implicit_lambda/)

This package adds support for implicit lambdas, so you can write `map(_ + 5, a_list)` instead of `map(lambda x: x + 5, a_list)`.

The code uses Python 3.7 features for brevity. The package could easily be made to work with earlier version. Please submit an issue if there is need.

Implicit lambdas are implemented using code generation. They are as fast as regular lambdas when running python with `-O` to enable optimizations.

```
--------------------------------------------- benchmark: 3 tests -----------------------------------
Name (time in ns) Mean StdDev Median OPS (Mops/s)
----------------------------------------------------------------------------------------------------
test_normal_lambda 196.3468 (1.01) 140.7775 (2.32) 166.9600 (1.0) 5.0930 (0.99)
test_il_lambda 196.6705 (1.01) 113.9049 (1.88) 171.6000 (1.03) 5.0846 (0.99)
test_op_chain 195.0673 (1.0) 60.6268 (1.0) 176.2300 (1.06) 5.1264 (1.0)
----------------------------------------------------------------------------------------------------
```
`il_lambda` uses implicit lambdas. `normal_lambda` uses a regular lambda. `op_chain` uses functools.partial and the operator module.

Without `-O`, lambdas with a more verbose `repr` are created:

```python
assert repr(_ + 5) == "<LambdaDSL: lambda x: (x + 5) @ {}>"
```

This results in up to 20% slower execution for very simple expressions. (A new type is created on the fly to hold the expression and resolving a call using a custom `__call__` is sufficient to incur such a penalty.)

For more complex expressions, the overhead will become negligible.

Python expressions are fully wrapped, including index operations `[]` (using `__getitem__`), member access (using `__getattribute__`) and any calls (`__calls`). This results in great flexibility.

To disambiguate between calls within the lambda and calling a lambda, implicit lambdas have to be explicitly converted into a callable/regular Python lambda.

`to_lambda` turns an implicit lambda expression into a Python lambda.

`auto_lambda` adds support for implicit lambdas to existing functions that take callables.

Wrapped versions of `builtin`, `functools` and `itertools` are provided out-of-the-box.

## Installation

To install using pip, use:

```
pip install implicit_lambda
```

To run the tests, use:

```
python setup.py test
```

## Example

To enable implicit lambdas, import placeholder symbols as needed and import wrapped builtin functions to use implicit lambdas interchangably with regular ones.

Usually, `to_lambda` and other helper functions don't need to be called.

```python
from implicit_lambda import _, x, y, to_lambda
from implicit_lambda.builtins import map
```

Implicit lambda provides wrappers around all common builtins.

```python
a_list = list(range(10))

mapped_list = map(_ + 2, a_list)

assert list(mapped_list) == list(range(2, 12))
```

There are also wrappers that turn builtins into lazy functions. A wrapped function provides a `._` version that can be used within an implicit lambda.

```python
mapper = to_lambda(map._(x + 2, _))

mapped_list = mapper(a_list)

assert list(mapped_list) == list(range(2, 12))
```

Implicit lambdas supports nested expressions

```python
mapped_list = map((_ << 3) * 3 - 23 * _ + 2, a_list)

assert list(mapped_list) == list(range(2, 12))
```

More useful reprs are available in __debug__ mode (just don't use `-O` when running python):

```python
another_lambda = to_lambda((_ << 3) * 3 - 23 * _ + 2)
assert repr(another_lambda) == "<lambda x: ((((x << 3) * 3) - (23 * x)) + 2) @ {}>"
```

or:

```python
assert (repr((_ << 3) * 3 - 23 * _ + 2) ==
"<LambdaDSL: lambda x: ((((x << 3) * 3) - (23 * x)) + 2) @ {}>)"
```

Implicit lambdas support multiple arguments, too:

```python
assert to_lambda(x * y)(5, 3) == 15
```

## Performance measurement

Run

```python
python -O -m pytest -k test_performance --benchmark-warmup=on --benchmark-autosave --benchmark-disable-gc
```

## Advanced features

Some operators are not supported directly because returning LambdaDSL would not work (`__bool__`, `__contains__`) or it could cause issues (`__repr__`).

There are wrapped builtins (`bool._` and `repr._`) that can be used instead, or helper functions like `contains` and `not_contains` that are exported from `implicit_lambda`.

Additionally, `implicit_lambda` supports custom `arg_resolvers` that map placeholders to lambda function arguments. By default, `strict_resolver` is used. There is also `from_allowed_signatures`, which picks the first argument signature that contains all used placeholder arguments, and `flexible_args`, which supports more than a required number of arguments and a custom partial ordering of placeholders in the case of conflicts.


Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

implicit_lambda-0.4.0.tar.gz (21.5 kB view details)

Uploaded Source

Built Distribution

implicit_lambda-0.4.0-py3-none-any.whl (42.6 kB view details)

Uploaded Python 3

File details

Details for the file implicit_lambda-0.4.0.tar.gz.

File metadata

  • Download URL: implicit_lambda-0.4.0.tar.gz
  • Upload date:
  • Size: 21.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.21.0 setuptools/40.6.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.1

File hashes

Hashes for implicit_lambda-0.4.0.tar.gz
Algorithm Hash digest
SHA256 7b80b5dde454e5bdc7589e68cf804ce09f1a7dd1504ccf5942731f24761ed1da
MD5 bbf159bef9d2798d494338ccccbe4f71
BLAKE2b-256 ef2ebf8c45cbf6f75bb57d9798ccbd17d8487e0f8a4310d9f9dffd1d3a51490b

See more details on using hashes here.

File details

Details for the file implicit_lambda-0.4.0-py3-none-any.whl.

File metadata

  • Download URL: implicit_lambda-0.4.0-py3-none-any.whl
  • Upload date:
  • Size: 42.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.21.0 setuptools/40.6.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.1

File hashes

Hashes for implicit_lambda-0.4.0-py3-none-any.whl
Algorithm Hash digest
SHA256 7e444975f3dcb072ece748129463f378882504d970bc63668aa5d4b87677101e
MD5 bb79667e75a2bf32df581a488bbcf8bf
BLAKE2b-256 03fd9264bb3a936c8315f98f3cd237c8770515770bca3b1d0925fe5a0cd9e23d

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page