Skip to content

Instantly share code, notes, and snippets.

@Bike
Created November 8, 2020 21:02
Show Gist options
  • Save Bike/e8d06bd99f83f9726e454376448c04d9 to your computer and use it in GitHub Desktop.
Save Bike/e8d06bd99f83f9726e454376448c04d9 to your computer and use it in GitHub Desktop.
type macros CL language extension

Proposal "typexpand"

Problem Description:

Common Lisp defines a macro mechanism for types, and the glossary mentions a "type expansion", but access to these by programmers is limited. Programmers cannot determine if a type specifier is a derived type, or the expansion of that type.

Some metaprogramming projects could use this information. Implementation-specific type expansion functions are used by, for example, Jan Moringen's configuration.options project, Massimiliano Ghilardi's cl-parametric-types project, and Masataro Asai's type-i project.

Proposal:

Add the functions described below.

Edit the glossary entry on "type expand" to indicate that this is now provided.

Rationale:

This improves the regularity of the language, and expands the capability of advanced programmers to do metaprogramming related to types.

Current Practice:

SBCL exports these functions in its sb-ext package, as well as an additional typexpand-all.

Cost to Implementors:

All implementations already include some means of macroexpanding types to implement Common Lisp, but several do not export a simple interface like this. For example, CCL and CMUCL only have ccl::%deftype-expander and ext:info (respectively) enabling access to type macro functions, which must be called manually. In short, some implementations would have to do a bit of work to present this clean interface.

Cost to Users:

Assuming the new symbols weren't added as external to the CL package, none. The change would be perfectly compatible.

Cost of Non-Adoption:

Some metaprogrammers have to rely on unstable and/or implementation-specific interfaces to accomplish something any implementation can do easily.

Benefits:

Some metaprogrammers will not have to rely on unstable and/or implementation-specific interfaces to accomplish something any implementation can do easily.

Aesthetics:

This is obviously closely analogous to macroexpand and macroexpand-1, clarifies the type system, and allows programmers uniform access to something implementations necessarily mostly have already.

Discussion:

type-expand(-1) may be better names.


Function TYPEXPAND, TYPEXPAND-1

Syntax:

typexpand type-specifier &optional env => expansion, expanded-p

macroexpand-1 type-specifier &optional env => expansion, expanded-p

Arguments and Values:

type-specifier---a type specifier.

env---an environment object. The default is nil.

expansion---a type specifier.

expanded-p---a generalized boolean.

Description:

typexpand and typexpand-1 expand type macros.

If type specifier is a macro type specifier, then macroexpand-1 expands the macro type specifier once.

typexpand repeatedly expands type specifier until it is no longer a macro type specifier. In effect, typexpand calls typexpand-1 repeatedly until the secondary value it returns is nil.

If type specifier is a macro type specifier, then the expansion is a macro expansion and expanded-p is true. Otherwise, the expansion is the given type specifier and expanded-p is false.

Macro expansion is carried out as follows. Once typexpand-1 has determined that the type specifier is a macro type specifier, it obtains an appropriate expansion function as by type-macro-function. This function is called with two arguments: the type-specifier, and the env. The value returned from this call is taken to be the expansion of the type specifier. [paragraph only included if proposal "type-macro-function" is adopted]

In addition to macro definitions in the global environment, any local macro definitions established within env by typelet are considered. If only type specifier is supplied as an argument, then the environment is effectively null, and only global macro definitions as established by deftype are considered. [paragraph only included if proposal "typelet" is adopted]

Examples:

;; Note: We use PAIR instead of CONS because an implementation is permitted
;; to define CONS as a macro type specifier itself.
(deftype list-of (&rest elements)
  (if (null elements)
      'null
      `(pair ,(first elements) (list-of ,@(rest elements)))))
(deftype list-n (n &optional element-type)
  `(list-of ,@(make-list n :initial-element element-type)))

(typexpand-1 '(list-n 2 integer)) => (LIST-OF INTEGER INTEGER), true
(typexpand '(list-n 2 integer)) => (PAIR INTEGER (LIST-OF INTEGER)), true
(typexpand 'not-a-macro) => NOT-A-MACRO, false
(typexpand-1 '(not-a-macro integer)) => (NOT-A-MACRO INTEGER), false

Affected By:

deftype, setf of type-macro-function [if proposal is adopted], typelet [if proposal is adopted]

Exceptional Situations: None.

Proposal "type-macro-function"

Problem Description:

The type macro mechanism is opaque compared to the form macro mechanism. deftype is described as defining a function to perform the expansion, but this function is not accessible to programmers or defined in detail.

Proposal:

Add one glossary entry and one accessor, below. Add text to CLHS 4.2.3 "Type Specifiers" explaining the type macro system in more detail.

Rationale:

Makes the language more uniform.

Current Practice:

Implementations must store type macro functions in general because that is the only way to implement deftype. However, they may not use this calling convention, or may pick off special cases (e.g. constant type expansions), which this proposal would complicate.

Cost to Implementors:

Implementors must uniformize their treatment of type macro functions, e.g. so that they accept the given arguments.

Cost to Users:

None. Most users could safely ignore this.

Cost of Non-Adoption:

Little to none.

Benefits:

Makes the language more uniform. Gives a useful capability to the few that would need it.

Aesthetics:

This is directly analogous to the situation with form macros.

Discussion:

It's debatable whether *macroexpand-hook* ought to be required. Currently, the hook takes a form, and type specifiers are not forms.


type macro function n. a function of two arguments, a type specifier and an environment, that implements type expansion by producing a type specifier to be used in place of the original argument type specifier.

Accessor TYPE-MACRO-FUNCTION

Syntax:

type-macro-function name &optional environment => function

(setf (type-macro-function name &optional environment) new-function)

Arguments and Values:

name---a symbol.

environment--- an enviornment object.

function, new-function---a type macro function, or nil.

Description:

Accesses the type macro function named name, if any, in the environment.

A value of nil denotes the absence of a type macro function named name.

Examples: None.

Affected By: None.

Exceptional Situations:

The consequences are undefined if environment is non-nil in a use of setf of type-macro-function.

See Also:

deftype

Notes: None.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment