Lambdex
Write complicated anonymous functions beyond lambdas in Python.
Install / Use
/learn @hsfzxjy/LambdexREADME
lambdex allows you to write multi-line anonymous function expression (called a lambdex) in an idiomatic manner. Below is a quick example of a recursive Fibonacci function:
def_(lambda n: [
if_[n <= 0] [
raise_[ValueError(f'{n} should be positive')]
],
if_[n <= 2] [
return_[1]
],
return_[callee_(n - 1) + callee_(n - 2)]
])(10) # 55
Compared with ordinary lambda, which only allows single expression as body, lambdex may contain multiple "statements" in analogue to imperative control flows, whilst does not violate the basic syntax of Python.
<details open> <summary> <em>Table of Content</em></summary>- More about lambdex
- WHAT'S NEW
- Installation & Usage
- Language Features
- Detailed Compile-time and Runtime Error
- EDGE CASES
- Runtime Efficiency
- Customization
- Code Formatting
- Known Issues & Future
- Q & A
- License
More about lambdex
An anonymous function is a function definition that is not bound to an identifier, which is ubiquitous in most languages with first-class functions. The language feature could be handy for logics that appear to be short-term use, and therefore adopted widely in some functional programming paradigms.
Python provides lambda <arg>: <expr> for such purpose. Lambdas are good for simple functionalities, but appear limited if logical complexity goes up. Consequently, higher-order functions (e.g., decorators) are often implemented as nested named functions, which is not concise enough.
lambdex as an experimental complement to lambdas, aims to provide a syntax similar to Python for anonymous functions. The syntax itself is built upon valid Python expressions, and therefore requires no modification to the interpreter. This package transpiles lambdexes into Python bytecodes at runtime, and therefore ensures the efficiency.
Installation & Usage
You can install lambdex from PyPI by
pip install pylambdex
or from Github by
pip install git+https://github.com/hsfzxjy/lambdex
To use lambdex, a simple import is required:
from lambdex import def_
my_sum = def_(lambda a, b: [
return_[a + b]
])
That's it! You don't even need to import other keywords such as return_.
Language Features
We are going to explore a wide range of features supported by lambdex in the following sections.
Parameters
The parameter declaration of lambdexes appears after the lambda. The syntax supports most variants of declaration just as ordinary functions.
# ordinary parameters
def_(lambda a, b: [...])
# parameters with default values
def_(lambda a, b=1: [...])
# starred arguments
def_(lambda *args, **kwargs: [...])
# keyword-only arguments
def_(lambda *, a, b: [...])
# positional-only arguments (Python 3.8+)
def_(lambda a, b, /: [...])
</details>
Variable assignment
Lambdexes use < instead of = for assignments, since = in Python is valid only in statements.
def_(lambda: [
foo < "bar",
])
</details>
<details>
<summary><em>show equivalent function</em></summary>
def anonymous():
foo = "bar"
</details>
< is chainable like ordinary =.
def_(lambda: [
foo < baz < "bar",
])
</details>
<details>
<summary><em>show equivalent function</em></summary>
def anonymous():
foo = baz = "bar"
</details>
Note that < has a higher precedence than not, and, or and if...else.... R-value with these operators should be enclosed by parentheses:
def_(lambda: [
foo < (a or b and not c),
foo < (a if cond else b),
])
</details>
Tuple destruction is also supported:
<details open> <summary><em>show code</em></summary>def_(lambda: [
(a, b) < (b, a),
(a, *rest, c) < [1, 2, 3],
])
</details>
In Python 3.8 or above, the walrus operator := might also be used. But be careful that Python enforces parentheses around := in many cases.
def_(lambda: [
foo := "bar", # OK
foo := baz := "bar", # syntax error
foo := (baz := "bar"), # OK
if_[condition] [
foo := "bar", # syntax error
(foo := "bar"), # OK
]
])
</details>
Augmented assignment
The augmented assignments are written as [op]_<, for example, +_< for +=. The snippet below illustrates all supported augmented assignments:
def_(lambda: [
a +_< 1,
a -_< 1,
a *_< 1,
a /_< 1,
a //_< 1,
a @_< 1,
a %_< 1,
a <<_< 1,
a >>_< 1,
a **_< 1,
a &_< 1,
a |_< 1,
a ^_< 1,
])
</details>
<details>
<summary><em>show equivalent function</em></summary>
def anonymous():
a += 1
a -= 1
a *= 1
a /= 1
a //= 1
a @= 1
a %= 1
a <<= 1
a >>= 1
a **= 1
a &= 1
a |= 1
a ^= 1
</details>
Conditional statement
Lambdexes use if_, elif_ and else_ for conditional control flows.
def_(lambda: [
if_[condition_1] [
...,
].elif_[condition_2] [
...,
].else_[
...,
]
])
</details>
<details>
<summary><em>show equivalent function</em></summary>
def anonymous():
if condition_1:
...
elif condition_2:
...
else:
...
</details>
Looping
Lambdexes support the two kinds of looping by keywords for_ and while_.
def_(lambda: [
# for...in...else...
for_[i in range(10)] [
print_(i),
].else_[
print("the optional else clause"),
],
# while...else...
while_[condition] [
...,
].else_[
print("the optional else clause"),
]
])
</details>
<details>
<summary><em>show equivalent function</em></summary>
def anonymous():
# for...in...else...
for i in range(10):
print(i)
else:
print("the optional else clause")
# while...else...
while condition:
print("the optional else clause")
</details>
break_ and continue_ are also supported.
def_(lambda: [
for_[i in range(10)] [
if_[i >= 5] [
break_
].else_[
continue_
]
]
])
</details>
<details>
<summary><em>show equivalent function</em></summary>
def anonymous():
for i in range(10):
if i >= 5:
break
else:
continue
</details>
With statement
With statements are supported by the with_ keyword. The optional as is written using >.
def_(lambda: [
# simple `with`
with_[open("foo")] [
...
]
# `with` with `as`
with_[open("foo") > fd] [
...
]
# multiple `with`
with_[open("foo"), open("bar") > fd] [
...
]
])
</details>
<details>
<summary><em>show equivalent function</em></summary>
def anonymous():
# simple `with`
with open("foo"):
...
# `with` with `as`
with open("foo") as fd:
...
# multiple `with`
with open("foo"), open("bar") as fd:
...
</details>
Try statement
The ordinary try statements are supported by keywords try_, except_, else_ and finally_.
def_(lambd
