The named-closure Reference Manual

Table of Contents

Next: , Previous: , Up: (dir)   [Contents][Index]

The named-closure Reference Manual

This is the named-closure Reference Manual, version 0.0.1, generated automatically by Declt version 3.0 "Montgomery Scott" on Sun May 15 05:39:19 2022 GMT+0.


Next: , Previous: , Up: Top   [Contents][Index]

1 Introduction

#+TITLE: NAMED-CLOSURE - introspectable and redefinable closures
* Why?
  Hot take: closures in Common Lisp and most lisps are broken for
  long-running images. (Maybe more so in non-lisps, but who cares.)

  Comparing to function symbols, closures are nearly unusable in the
  following aspects:
  
  1. They're hard to introspect, both from a user aspect and (even
     more so) programmatically. SBCL for example allows you to
     retrieve values in the flat closure, but do not save variable
     information.
     
  2. As a consequence, they in general don't have readable print
     syntax, and it might be impossible to write one. Function symbols
     on the other hand can be print and then read to get the exactly
     same object.
     
  3. They're near impossible to redefine. For a function symbol,
     setting its function cell causes all call site to consistently
     call the new definition. This is impossible for closures.

     Why redefining closure? It allows you to fix buggy code for
     closure (nobody always write correct code the first
     time!). Without redefinability you'll have closure with wrong
     code floating around in the image that is almost impossible to
     fix, unless you remember all the location the closure is used and
     fix it manually -- I find it much less pleasant and isn't always
     possible.

  Closures are still useful because:
  
  1. Concise syntax.
     
  2. They're the lingua franca for a whole bunch of "functional
     programs", which expect objects that are funcallable.

* What
  ~NAMED-CLOSURE~ provides ~DEFNCLO~ and ~NCLO~.

  - ~(nclo NAME LAMBDA-LIST . BODY)~ is similar to ~(lambda
    LAMBDA-LIST . BODY)~, but returns a funcallable object with slots
    corresponding to free variable in ~BODY~, has readable print
    syntax, and if ~nclo~ with the same ~NAME~ is encountered (for
    example, if re-evaluated from REPL), the function definition of
    all such funcallable objects is updated. Closed variables with the
    same names are carried over across update.

  - ~defnclo~
    #+BEGIN_SRC lisp
      (defnclo something (lambda-list-1...)
          (lambda-list-2...)
        body...)
    #+END_SRC
    is similar to
    #+BEGIN_SRC lisp
      (defun make-something (lambda-list-1...)
        (lambda (lambda-list-2...)
          body...))
    #+END_SRC
    except that ~make-something~ now returns a funcallable object with
    slots corresponding to variables declared in ~lambda-list-1~, has
    readable print syntax, and re-evaluating the ~defnclo~ updates the
    function definition of all such funcallable objects. Closed
    variables with the same names are carried over across update.

  Note: newly introduced variables are unbound for updated old
  closures! This will likely cause an ~unbound-slot~ condition when
  such closure is called. You're free to use the ~store-value~ restart
  your implementation (usually) provides to fix up the closure if
  possible.  There isn't anything more we can help ~:/~

* !!!Caveat!!!
  Saying ~nclo~ is similar to ~lambda~ is *a lie*. Currently, ~nclo~
  effectively copies the captured environment instead of directly
  link to it. See [[https://github.com/BlueFlo0d/named-closure/issues/1]]
  to understand the important behavioral difference between ~nclo~ and ~lambda~.

** Excuses

   It is possible to simulate the "correct" environment sharing behavior
   identical to ~lambda~. I'm currently not doing it because
   - It complicates introspection and proper readable printing
   - It nukes upgradability. While it's not unreasonable to ask user
     to fix up old closures using ~store-value~, it sounds pretty
     impratical to expect user to fix the sharing structure between
     different closures.
   - Closures are convenient. But remember we have *real objects*!
     If you need to rely on multiple closures sharing one environments,
     maybe it's better to just use CLOS.
   
* Example
  #+BEGIN_SRC lisp
    (use-package :named-closure)
    (defun make-inc-1 (x) (nclo inc (y) (setf x (+ x y))))
    (defparameter *test-instance* (make-inc-1 5))
    *test-instance* ; => #.(MAKE-INC :X 5)
    (funcall *test-instance* 6) ; => 11
    (funcall *test-instance* 6) ; => 17
    (defun make-inc-1 (x) (nclo inc (y) (setf x (- x y)))) ; changed our mind!!!
    (funcall *test-instance* 6) ; => 11
    (funcall *test-instance* 6) ; => 5
  #+END_SRC

  p.s. I will probably ensure ~NAME-CLOSURE~ only ever exports obscure
  names, so it should be quite safe to ~use-package~ it!
  
* How
  Under the hood, ~defnclo~ defines a funcallable class named
  ~something~, which in turn indirect calls through its
  class-allocated ~'named-closure::code~ slot so that it is
  redefinable.

  ~nclo~ is implemented by walking ~BODY~ and collecting its free
  variables, then calling ~defnclo~ with the free variable list (with
  ~&key~ prepended) passed as ~LAMBDA-LIST-1~.

  Note: Because free variables are converted to keyword argument,
  their ~symbol-name~ must be distinct. Is this good enough?

  There's one subtlety involved with ~nclo~: ~nclo~ usually appears as
  a non-top-level form, but it needs to ensure creating a top-level
  function definition for ~NAME~ in the runtime environment. We do this
  by abusing ~load-time-value~.  
  


Next: , Previous: , Up: Top   [Contents][Index]

2 Systems

The main system appears first, followed by any subsystem dependency.


Previous: , Up: Systems   [Contents][Index]

2.1 named-closure

Maintainer

Qiantan Hong <qhong@alum.mit.edu>

Author

Qiantan Hong <qhong@alum.mit.edu>

License

GPLv3+

Description

Named closures

Version

0.0.1

Dependencies
Source

named-closure.asd (file)

Component

named-closure.lisp (file)


Next: , Previous: , Up: Top   [Contents][Index]

3 Files

Files are sorted by type and then listed depth-first from the systems components trees.


Previous: , Up: Files   [Contents][Index]

3.1 Lisp


Next: , Previous: , Up: Lisp files   [Contents][Index]

3.1.1 named-closure.asd

Location

named-closure.asd

Systems

named-closure (system)


Previous: , Up: Lisp files   [Contents][Index]

3.1.2 named-closure/named-closure.lisp

Parent

named-closure (system)

Location

named-closure.lisp

Packages

named-closure

Exported Definitions
Internal Definitions

Next: , Previous: , Up: Top   [Contents][Index]

4 Packages

Packages are listed by definition order.


Previous: , Up: Packages   [Contents][Index]

4.1 named-closure

Source

named-closure.lisp (file)

Use List

closer-common-lisp

Exported Definitions
Internal Definitions

Next: , Previous: , Up: Top   [Contents][Index]

5 Definitions

Definitions are sorted by export status, category, package, and then by lexicographic order.


Next: , Previous: , Up: Definitions   [Contents][Index]

5.1 Exported definitions


Next: , Previous: , Up: Exported definitions   [Contents][Index]

5.1.1 Macros

Macro: defnclo NAME LAMBDA-LIST-1 LAMBDA-LIST-2 &body BODY

Defines a named closure type.

Similar to
(defun make-NAME LAMBDA-LIST-1
(lambda LAMBDA-LIST-2 . BODY))

except that ‘make-NAME’ now returns a funcallable object with slots corresponding to variables declared in LAMBDA-LIST-1, has readable print syntax, and re-evaluating the DEFNCLO updates the function definition of all such funcallable objects. Closed variables with the same names are carried over across update.

Package

named-closure

Source

named-closure.lisp (file)

Macro: nclo NAME LAMBDA-LIST &body BODY

Similar to (lambda LAMBDA-LIST . BODY).

Returns a funcallable object with slots corresponding to free variable in BODY, has readable print syntax, and if ‘nclo’ with the same NAME is encountered (for example, if re-evaluated from REPL), the function definition of all such funcallable objects is updated. Closed variables with the same names are carried over across update.

Package

named-closure

Source

named-closure.lisp (file)


Previous: , Up: Exported definitions   [Contents][Index]

5.1.2 Classes

Class: nclo ()
Package

named-closure

Source

named-closure.lisp (file)

Direct superclasses

funcallable-standard-object (class)

Direct slots
Slot: code
Allocation

:class


Previous: , Up: Definitions   [Contents][Index]

5.2 Internal definitions


Next: , Previous: , Up: Internal definitions   [Contents][Index]

5.2.1 Special variables

Special Variable: *inhibit-walker-eval-load-time-value*

Mega Haxx!

Package

named-closure

Source

named-closure.lisp (file)


Previous: , Up: Internal definitions   [Contents][Index]

5.2.2 Functions

Function: lambda-list-fvs LAMBDA-LIST
Package

named-closure

Source

named-closure.lisp (file)

Function: lambda-list-serialize-form LAMBDA-LIST
Package

named-closure

Source

named-closure.lisp (file)

Function: make-function-name SYMBOL
Package

named-closure

Source

named-closure.lisp (file)

Function: walk-fvs FORM ENV
Package

named-closure

Source

named-closure.lisp (file)


Previous: , Up: Top   [Contents][Index]

Appendix A Indexes


Next: , Previous: , Up: Indexes   [Contents][Index]

A.1 Concepts

Jump to:   F   L   N  
Index Entry  Section

F
File, Lisp, named-closure.asd: The named-closure․asd file
File, Lisp, named-closure/named-closure.lisp: The named-closure/named-closure․lisp file

L
Lisp File, named-closure.asd: The named-closure․asd file
Lisp File, named-closure/named-closure.lisp: The named-closure/named-closure․lisp file

N
named-closure.asd: The named-closure․asd file
named-closure/named-closure.lisp: The named-closure/named-closure․lisp file

Jump to:   F   L   N  

Next: , Previous: , Up: Indexes   [Contents][Index]

A.2 Functions

Jump to:   D   F   L   M   N   W  
Index Entry  Section

D
defnclo: Exported macros

F
Function, lambda-list-fvs: Internal functions
Function, lambda-list-serialize-form: Internal functions
Function, make-function-name: Internal functions
Function, walk-fvs: Internal functions

L
lambda-list-fvs: Internal functions
lambda-list-serialize-form: Internal functions

M
Macro, defnclo: Exported macros
Macro, nclo: Exported macros
make-function-name: Internal functions

N
nclo: Exported macros

W
walk-fvs: Internal functions

Jump to:   D   F   L   M   N   W  

Next: , Previous: , Up: Indexes   [Contents][Index]

A.3 Variables

Jump to:   *  
C   S  
Index Entry  Section

*
*inhibit-walker-eval-load-time-value*: Internal special variables

C
code: Exported classes

S
Slot, code: Exported classes
Special Variable, *inhibit-walker-eval-load-time-value*: Internal special variables

Jump to:   *  
C   S  

Previous: , Up: Indexes   [Contents][Index]

A.4 Data types

Jump to:   C   N   P   S  
Index Entry  Section

C
Class, nclo: Exported classes

N
named-closure: The named-closure system
named-closure: The named-closure package
nclo: Exported classes

P
Package, named-closure: The named-closure package

S
System, named-closure: The named-closure system

Jump to:   C   N   P   S