Dec. 10th, 2022

pnathan: elephant bypasses fence to drink from pool (Default)
Another entry in the "simplified defclass" reckoning:


(defmacro defstruct* (name &rest slots)
  "Provides a DEFSTRUCT interface to a DEFCLASS"
  (flet
      ((accessor-symbol
           (sym)
         (intern (concatenate 'string (string name) "-" (string sym)))))
    (let* ((actual-slots
             (etypecase
                 (car slots)
               (string
                (cdr slots))
               (symbol
                slots)
               (cons
                slots)))
           (slot-list
             (loop for s in actual-slots
                   collect
                   (etypecase s
                     (symbol
                      `(,s
                        :initarg ,s
                        :accessor ,(accessor-symbol s)))
                     (cons
                      `(,(car s)
                        :initarg ,(car s)
                        :accessor ,(accessor-symbol (car s))
                        :initform ,(cadr s))))
                   )))

      `(defclass ,name ()
         ,slot-list
         (:documentation ,(etypecase (car slots)
                            (string
                             (car slots))
                            (symbol
                             (format nil "The struct defining ~a, containing slots ~{~a~^, ~}" name slots))))
         ))))




I like defstruct, a lot, in terms of its speed and simplicity of interface. But it gets rather miserable when you want to integrate with CLOS IME. So, another stab at it (previous stab was https://github.com/pnathan/defobject ). I think this one is considerably cleaner in stylistics.
pnathan: elephant bypasses fence to drink from pool (Default)

Common Lisp is the dynamic language I keep returning to. Garbage collection; a reasonably large amount of libraries; not-absurd policies around exceptions (hi Perl), not-absurd design around power (hi Python), not-absurd design around formatting & naming (hi Go).

Scala is, plausibly, at this point, the only other major language I rather like hacking in, and it's JVM tied (sigh), slow to compile (sigh), and frankly rather verbose.

And, too, the advanced-type languages often turn into a game of "lets defeat the type system so it works right". That's not to knock Rust or its less popular peers - Nim, Pony - but it's just, after a while, a drag.

The basic problem with Common Lisp is the lack of effective static assertions - this isn't so bad when you're in the moment, but when the codebase gets larger and you wind up modifying things "far away" - or you come back to the project months later - things don't work quiiite as well.

I will have to keep grinding on this little problem, but I think I will need to cajole up a basic solution:

A dynamic-binding macro system which acts as a type asserter that acts at eval-time time to check that the current args seem to be valid.

More sophisticatedly, this would have to be a treewalker that finds, expands, and checks function invocations.

Essentially, if I write

(DEFUN-TYPED foo ((x int) (y funny-object)) (compute-funny y (+ x 1)))

then the -TYPED system should validate that yes, x is doing int-y things and yes, compute-funny takes a funny object in the second position - and this should happen at eval-time.

If, however, some dynamic binding *no-typed-assert is turned off, then at eval time this should not occur.

The previous effort I know about for Common Lisp gradual typing is Coalton, which is a sort of library-in-CL, which turned into its own CL-hosted language. Meh. Then there's https://github.com/mmontone/cl-gradual, which seems to be sort of off and on.

Anyway. I might use one of those, or cobble together some 'orrible 'ack myself.

Most Popular Tags

Expand Cut Tags

No cut tags
Page generated Jul. 11th, 2025 11:42 pm
Powered by Dreamwidth Studios