Pyli/Apply Args

From Jonathan Gardner's Tech Wiki
Jump to: navigation, search

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).