On Thu, 2021-04-01 at 23:12 -0700, Aleix Conchillo Flaqué wrote: > Hi Maxime, > > Thank you for your comments! > > On Thu, Apr 1, 2021 at 4:37 AM Maxime Devos wrote: > > > For example, in: > > > > > (define (%test-comp2 comp x) > > > (syntax-case (list x (list (syntax quote) (%test-source-line2 x)) comp) () > > > (((mac tname expected expr) line comp) > > > (syntax > > > - (let* ((r (test-runner-get)) > > > - (name tname)) > > > + (let ((r (test-runner-get))) > > > (test-result-alist! r (cons (cons 'test-name tname) line)) > > > (%test-comp2body r comp expected expr)))) > > > > I would keep the let* (but reverse the binding order), but change 'tname' > > with 'name' in the call to 'test-result-alist!', such that 'test-X' macros > > behave somewhat more like procedure calls (except for installing exeption > > handlers and having access to the s-expression of the code that will be run, > > of course). It's largely a matter of taste, though. > > > > I've done this change. One thing I don't understand is the "reverse > the binding order", I've done it as suggested but is this change the > one you refer to as "matter of taste"? Yes, that's the change I was referring to. As to why: a procedural equivalent of 'test-assert would look more or less like ;; (possibly more arguments are required) (define* (test-assert* name thunk expression) ;; THUNK: when called, return something that will be ;; used as true/false. ;; EXPRESSION: S-expression representing the body ;; of THUNK (let ((r (test-runner-get))) ;; evaluate (thunk) here within some exception ;; handlers and use r ...)) (Similar equivalents to test-equal, test-eq ... can be written as well.) Suppose '(test-assert* NAME (lambda () EXP) 'EXP) is evaluated. Then first NAME is evaluated, which can have side-effects. The lambda expression and (quote EXP) are evaluated as well, but no side-effects are possible here (aside for allocating some memory for the thunk, which can lead to a 'out-of-memory exception, but that's usually simply ignored). Only after the arguments are evaluated will '(test-runner-get) be evaluated. However, for the original test-assert macro, the evaluation order is different. From a REPL: > (use-modules (srfi srfi-26)) > ,expand (test-assert NAME EXP) ;; output manually cleaned up $6 = (let* ((r (test-runner-get)) (name NAME)) more code ....) It should be clear that here 'NAME is evaluated *after* '(test-runner-get) is evaluated, unlike for the 'test-assert* procedure. That said, SRFI-64 does not require NAME to be evaluated even if trying to get the test runner fails for some reason, I don't think anyone ever changes the test runner from within a ‘call’ (not really a call as test-assert is a macro) to test-assert, and in practice NAME is a constant, so in practice it doesn't really matter in what order things are evaluated. Also see next comment: > > In any case, it is good that 'tname' is now evaluated only once, as per > > SRFI-64 (notice ***It is evaluated only once.*** (markup mine)): > > > > [...] > Yes, this makes sense. Thanks again for pointing that out. This is done correctly in the new patch (and the old patch IIRC). Also, by reversing the binding order from - (let* ((r (test-runner-get)) - (name tname)) to + (let* ((name tname) + (r (test-runner-get))) the expression tname is also evaluated *at least* once, thus TNAME is evaluated *exactly* once, which seems like a nice property to have, though this is a bit stricter than SRFI-64 demands IIUC. Also, the formatting seems to have gone wrong. Shouldn't this be + (let* ((name tname) + (r (test-runner-get))) ? If in Emacs, I recommend scheme-mode, in which case pressing tab on the second line would produce the desired formatting. Alternatively, select a region of text and presss tab. > > As this patch does not ‘merely’ fix a warnings, but fixes a bug, could you change > > the patch message accordingly? Something like > > > > srfi-64: fix double evaluation of test-name. > > > > perhaps? > > The revised commit message looks good to me. Greetings, Maxime. p.s: I'm not a guile maintainer so you will have to wait on someone else to actually merge this.