cl-series
, vector multiplication, assert
and lisp interactivity normsI thought I should furnish our incredible discussion yesterday about common lisp macro assertables with Vassil Nikolov and Kent M. Pitman with my own practical example of assert
similar to the hyperspecâs example.
Vassil is working (when he has time) on an his assertables macro
s for hierarchical, programmatic control of including and excluding assertions in code.
I will write a trivial vector multiplication using cl-series
- i.e., a lazy, tight, declarative, generated in-order traversal, withstanding that this is a highschoolish task.
We will write a macro that generates a specific dimension vector multiplier, and assert
that arguementsâ dimensions are correct. The assertion will turn out to be important.
While writing this, it also occured to me that the interactive expert useage generated by lisp for me in order to help me in the context of my failed assertion is very important iconic classic lisp useage.
#|
(eepitch-sbcl)
(eepitch-kill)
(eepitch-sbcl)
(require :series)
(series::install)
|#
(equal ((generate-vector-multiplier (2))
'(1 2) '((3)
(4)))
(+ (* 1 3)
(* 2 4)))
(defmacro generate-vector-multiplier ((lengths))
`(lambda (a b)
(assert (and (equal (length a) ,lengths)
(equal (length b) ,lengths))
(a b)
"Dimension 1 of the first vector, ~d,
should be equal to dimension 2 of the second vector, ~d,
should be equal to the vector-multiplier's length, ~d."
(length a) (length b) ,lengths)
(let* ((series-a (scan a))
(series-b (scan b))
(series-bá” (#Mcar series-b))
(series-abá” (#M* series-a series-bá”)))
(collect-sum series-abá”))))
generate-vector-multiplier
for lengths 2
.CL-USER> (generate-vector-multiplier (2))
#<FUNCTION (LAMBDA (A B)) {5373E07B}>
CL-USER> (funcall * '(1 2) '((3) (4)))
11
Great. Also remember in lisp interaction, *
is âthe last first returnâ, **
is âthe second last first returnâ, ***
, third.
Looks good but what about (funcall ** '(1 2) '((3) (4) (5)))
? We end up interactively in lispâs debugger with the message we just wrote:
Dimension 1 of the first vector, 2,
should be equal to dimension 2 of the second vector, 3,
should be equal to the vector-multiplier's length, 2.
[Condition of type SIMPLE-ERROR]
Restarts:
0: [CONTINUE] Retry assertion with new values for A, B.
1: [RETRY] Retry SLIME REPL evaluation request.
2: [*ABORT] Return to SLIME's top level.
3: [ABORT] abort thread (#<THREAD "repl-thread" RUNNING {1001380003}>)
Backtrace:
0: (SB-KERNEL:ASSERT-ERROR (AND (EQUAL (LENGTH A) 2) (EQUAL (LENGTH B) 2)) (A B) "Dimension 1 of the first vector, ~d, ..)
1: ((LAMBDA (A B)) (1 2) ((3) (4) (5)))
2: (SB-INT:SIMPLE-EVAL-IN-LEXENV (FUNCALL ** (QUOTE (1 2)) (QUOTE (# # #))) #<NULL-LEXENV>)
3: (EVAL (FUNCALL ** (QUOTE (1 2)) (QUOTE (# # #))))
--more--
I clicked CONTINUE
:
CL-USER> (funcall ** '(1 2) '((3) (4) (5)))
The old value of A is (1 2).
Do you want to supply a new value? (y or n) n
The old value of B is ((3) (4) (5)).
Do you want to supply a new value? (y or n) y
Type a form to be evaluated:
#((3) (4))
11
You can also notice that I snuck in a vector (of lists) instead of a list; cl-series
operates on lisp sequences, not particularly just vectors or lists.
We find out that our assertion was critically important, because of a desireable property of cl-series
. Letâs make a version without the assertion of matching lengths:
(defmacro generate-jagged-vector-multiplier ((lengths))
`(lambda (a b)
(let* ((series-a (scan a))
(series-b (scan b))
(series-bá” (#Mcar series-b))
(series-abá” (#M* series-a series-bá”)))
(collect-sum series-abá”))))
CL-USER> (generate-jagged-vector-multiplier (2))
; caught STYLE-WARNING:
; The variable LENGTHS is defined but never used.
;
; compilation unit finished
; caught 1 STYLE-WARNING condition
GENERATE-JAGGED-VECTOR-MULTIPLIER
CL-USER> (generate-jagged-vector-multiplier (2))
#<FUNCTION (LAMBDA (A B)) {5373E7DB}>
CL-USER> (funcall * '(1 2) '((3) (4)))
11
Well, the warning kind of gives it away.
CL-USER> (funcall ** '(1 2) '((3) (4) (5)))
11
cl-series
lazily truncated the second vector without asking us. This is because series handles infinities for example through this laziness, cotruncating computation to the shortest participant (basically to implicitly cut out infinities).
However our intent here was vector multiplication, which is not defined on mismatched vector lengths. Furthermore, as we were warned above, our lengths
arguement above is now actually doing nothing:
CL-USER> (funcall *** '(1 2 3) '((3) (4) (5)))
26
We included an assertion when generating specific-length matrix multiplication lambdas per the hyperspec
(defmacro generate-vector-multiplier ((lengths))
`(lambda (a b)
(assert (and (equal (length a) ,lengths)
(equal (length b) ,lengths))
(a b)
"Dimension 1 of the first vector, ~d,
should be equal to dimension 2 of the second vector, ~d,
should be equal to the vector-multiplier's length, ~d."
(length a) (length b) ,lengths)
(let* ((series-a (scan a))
(series-b (scan b))
(series-bá” (#Mcar series-b))
(series-abá” (#M* series-a series-bá”)))
(collect-sum series-abá”))))
this generated a choice for me to interactively resolve some assertion-failing data (note that I didnât write these prompts):
CL-USER> (funcall ** '(1 2) '((3) (4) (5)))
<I select CONTINUE (with new values) from the interactive condition handler>
The old value of A is (1 2).
Do you want to supply a new value? (y or n) n
The old value of B is ((3) (4) (5)).
Do you want to supply a new value? (y or n) y
Type a form to be evaluated:
#((3) (4))
11
without the assertion, cl-series
was implicitly cotruncating the incorrect length vector - good for working with infinities, bad for our definition of vector multiplication.
What do you think about ANSI CLâs assertions, my example, and cl-series
â historic, modern, declarative, lazy evaluation? Also, thinking about it, I think the way common lisp is seen to actively engage with its expert lispuser about the failed assertion with the image still in the context that the assertion failed is an incredibly important useage.
Anyway, talk in the Mastodon thread please (or come on the lispy gopher show live with everyone).
I hope we can share and discuss these futuristic and bleeding edge ANSI common lisp features and concerns with everyone who kinda feels these ideas should have existed (and actually do) and even have been standardized with ANSI, and be actively pursued and expanded with substandards
like cl-series
and assertables
.
Please do share and disseminate this article and yesterdayâs interview and everything by whatever your personal channels are if you agree.
screwlisp proposes kittens