Anonymous - Mercerenies/alakazam GitHub Wiki
Anonymous Functions
Operators
The Alakazam placeholder objects can be used with the following operators. Aside from a few exceptions which are listed at the bottom, either side of the placeholder operator expression can be a constant without causing any issues.
Note that, by default, the placeholders _1
through _5
are
provided, for accessing the first five positional arguments of an
argument list. If more arguments are required, anonymous instances can
be explicitly constructed using zz.arg(n)
, which refers to the nth
positional argument (1-based) of an argument list, and
zz.kwarg(name)
, which refers to the keyword argument with the
specified name.
_1.property_name
_1[_2]
_1 + _2
_1 - _2
_1 * _2
_1 / _2
_1 // _2
_1 % _2
_1 ** _2
_1 << _2
_1 >> _2
_1 & _2
_1 ^ _2
_1 | _2
- _1
+ _2
_1 == _2
_1 != _2
_1 < _2
_1 <= _2
_1 > _2
_1 >= _2
Function Calls
Using the function call operator on placeholder objects requires a bit
of a different approach. Simply using _1(1)
will not produce a
function which calls its argument as a function; it will produce the
identity function and then call it with the argument 1
, because
placeholder objects are designed to be called using this syntax to
begin with. If you need to use function call syntax in your anonymous
lambda, you must wrap the function itself in a zz.bind
call.
# This defines a function which calls its first argument, passing 0 as a parameter.
zz.bind(_1)(0)
# This defines a function which calls foo, passing it the lambda argument
# as a keyword parameter.
zz.bind(foo)(keyword = _1)
Other than wrapping the function in zz.bind
, this syntax can be used
in the same way as any other operator. In particular, the function
name can be constant or an anonymous lambda, and the argument list can
contain positional and keyword arguments, which can also be either
constant values or anonymous objects themselves. So, for instance,
zz.bind(_1)(_2 + _3, "r", os = _4, lang = "Python")
is a valid
anonymous object, although it is not recommended that you use
zz.bind
for anything nearly this complex.
There is an alternative, and often cleaner, syntax for
zz.bind
. Instead of calling the operator and calling the returned
value, simply pass the partial application arguments to zz.bind
. So
the following are equivalent.
zz.bind(_1)(_2, arg = _3)
zz.bind(_1, _2, arg = _3)
Note that this shortcut cannot be used if no arguments are being applied, so the following are not equivalent.
zz.bind(_1) # Returns a binding object
zz.bind(_1)() # Returns a function which calls its first argument
Assignment and Deletion
Alakazam also provides techniques for construction lambdas based on
assignment and deletion. This would normally be impossible in Python,
as assignment and deletion are statements and thus cannot be used in a
lambda expression. However, with Alakazam, it becomes possible using
the special helper functions zz.set
and zz.delete
. These two
functions are unique in that they expect their first argument to be of
a specific form and will raise an exception if the argument does not
match the expected syntax. The following are valid forms.
zz.set(a[idx], expr)
zz.set(a.name, expr)
zz.delete(a[idx])
zz.delete(a.name)
The two zz.set
calls construct lambdas which will behave as though
the right-hand-side expression was assigned to the left-hand-side
object with the =
operator, calling either __setitem__
or
__setattr__
on the receiving object. The two zz.delete
calls will
produce lambdas which behave as though the object had del
called on
it, calling either __delitem__
or __delattr__
as appropriate. Note
that the a
in these forms must be an anonymous lambda instance,
which means that if you wish to use a constant value in this slot, you
must wrap it in a zz.var
call.
# This produces a lambda which sets its argument's greeting to "Hello!".
zz.set(_1.greeting, "Hello!")
# This produces a lambda which deletes the first element of its argument.
zz.delete(_1[0])
# This produces a lambda which deletes the nth element of the list foo, where
# n is passed as an argument. Note the zz.var call to mark the constant value.
zz.delete(zz.var(foo)[_1])
Note that zz.assign
is a synonym for zz.set
. The former should not
be necessary unless you for some reason are importing Alakazam
unqualified and would like to use it in conjunction with the built-in
type set
.
Exceptions and Notes
- The
_1.property_name
syntax only works ifproperty_name
is not a magic method name and will fail if the anonymous function is called with an object that lacks a__dict__
. - The
_1[_2]
syntax will fail if the object being subscripted is not an anonymous object. So_1[some_expr]
will work butsome_expr[_1]
will not. In this case, explicitly wrapping the expression withzz.var
will solve the problem, as inzz.var(some_expr)[_1]
. - Due to limitations in Python's overloading capabilities, the chained
comparison operator syntax cannot be used with these placeholder
objects. So
_1 < _2
will work but_1 < _2 < _3
will produce a function that behaves in an undefined manner. - For consistency across different versions of Python, Alakazam will
always use Python 3 division semantics for its anonymous lambda
syntax. That is, when using Alakazam in Python 2, it will behave as
though
from __future__ import division
is in scope.
Remember, placeholder expressions are for very short lambdas. If
your expression is complicated enough that you have to wonder "Will
this work correctly?", it's probably complicated enough that you
should be explicitly using Python's lambda
keyword. For instance,
_1 + _2
is better than lambda x, y: x + y
, but (_1 + _2) % _3 > _1
should probably use an explicit lambda
, or even a named
function.