Pyli/Apply Args
Contents
Introduction
When you are defining a function (either through fn, defn, or some other method), you may specify the parameter template. This tells its callers how the parameters passed in will be used and what they mean.
Features
The parameter specification is very simple. It only allows the specification of positional parameters and a catch-all for the remaining parameters.
The parameter specification should be a vector, although technically, any iterable is tolerated. Please don't get fancy and specify a parameter specification that will change between invocations.
The expected items are simply names, names that the corresponding values will be assigned to. The final name may be a vector like (rest name), where name will contain the remaining parameters, if any.
It is not possible to specify optional parameters, default values, or named parameters with this method. Nor is it possible to do destructuring as in defmacro in Lisp with this method. Nor can you specify expected types of the parameters.
However, you can simply collect all the parameters in a single rest parameter and do processing on it as the first steps of the function, adding in destructuring, named parameters, optional parameters, and type checking as needed.
How it Works
apply-args takes the parameter specification given by the function or macro and the expression passed into that function or macro and produces a new namespace in which the body of that function or macro is executed.
Simple Ordered Parameters
You can limit the number of parameters:
- (a b c): Expects three expressions, assigned to a, b, and c.
You can have a variable number of parameters:
- (a b c rest d): Expects 3 or more expressions. The first three are assigned to a, b, and c. The last are assigned to d as a Vector.
Type Specification
You can specify what type of arguments you expect. For generic methods, this is important because it will affect which generic method gets called.
- ((number a) (list b) (dict c) * (list d)): a has to be number. b has to be list. c has to be a dict. d has to be a list, of course.
You can specify a template. This would be an object that looks like what is expected, or a description of it.
- (list of int *)
- (dict with keys (a b c) optional (d e f))
- '(int int int): List of 3 ints
- (list of (string int)*)
The function expect contains the implementation of this.
Optional Parameters
Optional parameters have a default specified.
- ((a = 5) (b = 7)): Takes 0, 1, or 2 parameters.
Named Parameters
Since parameters can be named, you may want the external name and the internal name to be different. Use the 'as' symbol to do this.
Generally, (type name [= default] [as alias])
Calling
- Calling regularly:
- (apply func (1 2 3))
- Inlining a list:
- (apply func (concat (1 2 3) args))
- Specifying keyword args:
- (apply-key func ordered-args dict-of-keyword-args)
Using It
The hard way:
(let (a b c) (apply-args (a b c) args) ... )
The easy way:
(let (apply-args (a b c) args) ...)
???: Should I use [...] whenever there is an expression that shouldn't be evaluated directly? Can that bypass macros?
destructure-apply-args
If you'd like to bypass the traditional apply-args, you can simply specify a single argument --- (rest args). Then pass this to destructure-apply-args:
(defun foo ((rest args)) (let (destructure-apply-args form args) ....))
Named Parameters
PyLi doesn't support named parameters like Python. If you want to achieve a similar effect, you can pass an array with an even number of elements. Each odd element is the
Additional apply args
It is possible to use your own apply-args, especially for macros. (Note that if you want named parameters, you can pass in a list of name-value pairs).