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.
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
macrosThe 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
.
deftype
-defined TypesCL 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.
structure-class
ClassesThe main problem that any implementation of the NEW API must
face is how to deal with structure-class
es. 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.
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> ] ')'
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>
structure-class
constructorsWhile 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.
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)
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.
The usual ones. Plus various past discussions on C.L.L. and elsewhere.
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.
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.