NEW: a new operator for Common Lisp

Common Lisp is a functional language (and also an imperative, object-oriented one, which, moreover, can be used in a declarative fashion) and it has a mode of operation where "objects" are created on the fly (on the heap, and then dispatched by the garbage collector). Object creation is done through a slew of ad-hoc constructors (think list, cons, make-array etc.), constructors tied to a specific structure and make-instance.

Alas, many "new" programming languages have settled on an operator that dynamically creates new objects in memory: new.

This library (which is nothing new) introduces the new and newq operators for Common Lisp. The idea is to provide an alternative to all (or most) ad-hoc constructors and make-instance, so that you can write something like the following:

    cl-prompt> (defclass foo () ())
    FOO

    cl-prompt> (new 'foo)
    #<FOO 42>
or, with a convenience macro
    cl-prompt> (newq foo)
    #<FOO 42>

Of course, this does not amount to much, yet, things become more interesting when you try things like the following:

    cl-prompt> (newq (array single-float (2 2)) '((1.0 0.0) (0.0 1.0)))
    #2A((1.0 0.0) (0.0 1.0))

    cl-prompt> (type-of *)
    (SIMPLE-ARRAY SINGLE-FLOAT (2 2))

    cl-prompt> (newq (array double-float) '((1.0 0.0) (0.0 1.0))
    Error: Attempt to set double-float array element to 1.0 of type SINGLE-FLOAT.

    cl-prompt> (new '(mod 42) (read))
    1024
    Error: 1024 is not of type (INTEGER 0 (42)).

The problem is that as soon as you wade in CL "type specifiers" land, you hit the dreaded "implementation dependent" wall, or, worse. the "this is not even mentioned in the ANSI spec" wall. Think of DEFSTRUCT and the fact that there is no portable way to get to the constructor, not to mention DEFTYPE.

Specification Rationale

The rationale for the specification of the new operator is simply to provide an operator that is familiar to many programmers and that seamlessly meshes in a regular CL environment. Ancillary functionalities are provided to make the new operator work for special (and not so special) cases.

The key and simple invariant that should always hold for the specification, the reference implementation and for any user-provided extension is the following:

    cl-prompt> (typep (new type ...) type)
    T

new and newq macros

The main operator new is a generic function with several methods defined (at least in the reference implementation). newq is a convenience macro that does not evaluate the first argument and simply expands in a call to new.

Defining Methods to Handle deftype-defined Types

CL types can be defined with deftype. The result is a type specifier that is either a symbol, or, most likely, a cons. The primary method for new cons eventually dispatches on the generic function type-cons, which mostly discriminates on eql specializers on the head of the type specifier.

Defining Methods to Handle structure-class Classes

The main problem that any implementation of the NEW API must face is how to deal with structure-classes. The CL specification does not make provisions for ways to fetch the constructors of a DEFSTRUCT, which can be many.

The NEW API provides two ways to deal with such a problem: (1) by introducing a modicum of new "syntax" that can be used to circumvent the problem, and (2) by providing a way to record the DEFSTRUCT constructors.

Extended "syntax" for structure-class and standard-class

In order to provide a way to specify the "constructor" (and therefore the standard one) in a call to new or newq for a structure-class the following syntax is introduced to specify a "pseudo type" clearly identifying strucutre types (and, simmetrically, class types); in some circuitous way this is akin to the C++ typename keyword.

   '(' structure <structure type designator> [ by <constructor> ] ')'

   '(' class <class designator> [ by <constructor> ] ')'

Example

    cl-prompt> (defstruct foo a s d)
    FOO

    cl-prompt> (new '(structure foo) :s 42)
    #S(FOO :A NIL :S 42 :D NIL)

    cl-prompt> (defstruct (bar (:constructor baryfy (a s))
			       (:constructor babar (a &optional (d (+1 a)))))
		   a s d)
    FOO

    cl-prompt> (new '(structure bar :by baryfy) "QWE" "RTY")
    #S(FOO :A "QWE" :S "RTY" :D NIL)

    cl-prompt> (newq (structure bar :by babar) 42)
    #S(FOO :A 42 :S NIL :D 43)

    cl-prompt> (defclass baz () ())
    BAZ

    cl-prompt> (newq (class BAZ))
    #<BAZ 42>

Recording structure-class constructors

While implementations must have a "generic" structure constructor to deal with the #S(STRUC-CLASS-NAME ...) syntax, alas, such functionality is not portably advertised. Because of this fact, in order to allow the "plain" use of constructors (i.e., without the wrapping (structure <s>)), new must rely on an explicit "recording" of a constructor for the structure, lest redefining the macro DEFSTRUCT. In order to register a constructor for the benefit of new the function register-structure-constructor is thus provided.

Example

   cl-prompt> (defstruct foo a s d)
   FOO

   cl-prompt> (register-structure-constructor 'foo #'make-foo)
   #<Function MAKE-FOO ...>

   cl-prompt> (new 'foo :s 42)
   #S(FOO :A NIL :S 42 :D NIL)

Downloads and Repositories

The NEW specification and reference implementation can be found at common-lisp.net. The implementation lies within a package nicknamed NEW-OP and is based on the generic functions new, type-cons, and the macro newq.

The reference implementation contains a number of predefined methods for new and type-cons that handle most of the basic CL types and classes.

The NEW-OP library is available from Quicklisp.

The specification and (reference) implementation can be found at common-lisp.net.

The git can be gotten from the common-lisp.net GitLab instance in the NEW-OP project page.

References

The usual ones. Plus various past discussions on C.L.L. and elsewhere.

Disclaimer

The code associated to these documents is not completely tested and it is bound to contain errors and omissions. This documentation may contain errors and omissions as well.

License

The code is released under a BSD license. See the file COPYING in the code tree. You are advised to use the code at your own risk. No warranty whatsoever is provided, the author will not be held responsible for any effect generated by your use of the library, and you can put here and around each piece of the library code the scariest extra disclaimer you can think of.