rackunit
rackunit copied to clipboard
add check-equal?/values and check-match/values
Related to #71. I put check-equal?/values
in a separate form because check-equal?
is meant to be a function-like check, and adding multiple values to the existing check-equal?
would require it to evaluate it's arguments differently from how a function would.
I don't understand why it would have to evaluate anything differently.
It has to evaluate them in the context of call-with-values
, so the function form can't have the same behavior.
What expression do you expect people to evaluate that distinguishes these two contexts? Yes, I agree there are theoretically some (things that call continuation-mark-set->context
, for example), but they don't seem like something that would ever show up inside a call to check-equal?
.
The check-eq?
and check-eqv?
checks need this too right? I'd find it strange if they weren't consistent with check-equal?
.
What about adding a values-list
syntax to define-check
that let you specify that some argument positions should "capture" multiple values:
(define-check (check-equal? (values-list actual) (values-list expected))
... compare actual and expected, which are each lists of values ...)
;; captures the values returned by both expressions and passes them as lists
(check-equal? (values 1 2) (values 1 3))
That way, the equal checks don't need to use define-syntax
and manually add check infos. And rackunit could not export the values-list
syntax binding to hide this feature from clients if that was desired.
But they still have to be almost "full-fledged" macros, in that they can't be function-like anymore. It won't be equivalent to say (#%app check-equal? actual-expr expected-expr)
versus (check-equal? actual-expr expected-expr)
if we do this.
I think it's a reasonable tradeoff. It's purely additive; no existing uses of check-equal?
would break as a result of adding this feature. Even existing higher order uses will continue to work as they always have.
And what if someone is testing functions that look at continuation-mark stuff?
(Edit: I can't find a way to detect it other than looking at the error message, so far.)
Would that work now?
I don't think that supporting people writing (#%app check-equal? ...)
is important, compared to being able to support people writing (check-equal? (f 1) (values 1 2))
for functions that return multiple values. Not even close!
And the earlier distinguishing context I gave was meant to reenforce how there aren't any interesting contexts that can distinguish them.
Here's something that distinguishes them:
#lang racket/base
(require rackunit)
(define fn-context (#%plain-app make-continuation-prompt-tag 'fn-context))
(define-syntax-rule (#%app f a ...)
(#%plain-app (with-continuation-mark fn-context 'function-position f)
(with-continuation-mark fn-context 'argument-position a)
...))
(define (stuff)
(let/cc k
(continuation-mark-set->list (continuation-marks k) fn-context)))
;; this passes:
(#%app check-equal? (stuff) '(argument-position))
;; this fails:
(check-equal?/values (stuff) '(argument-position))
(Edited to leave out unnecessary call-with-values)
Well, of course that does. If you write (m check-equal? a b)
you can make arbitrary changes to things. Here's another example that is just the same as yours and not something that I think is very relevant to this discussion.
#lang racket
(require rackunit)
(define-syntax-rule
(m x ...)
(printf "I am different because a macro rewrote me!\n"))
(m check-equal? 1 2)
(check-equal? 1 2)
Put another way, in case what I wrote was not clear: your example is not really about continuation marks. Here's another example to illustrate this point.
#lang racket
(require rackunit)
(define global 1234)
(define-syntax-rule
(m f x ...)
(f (let ([saved-value global])
(set! global 15)
(begin0 x
(set! global saved-value)))
...))
(m check-equal? global 1234)
(check-equal? global 1234)
I don't think this is relevant to check-equal?
at all.
Do (#%app check-equal? (stuff) '(argument-position))
and (#%app check-equal?/values (stuff) '(argument-position)))
have different results?
Okay. I just realized check-equal?
itself doesn't use the #%app
from its use site but the app from its definition site.
(Re: @jackfirth's comment)
The check-equal?/values
form wouldn't make sense as a function expression because functions can't accept multiple-valued arguments. I didn't want to pretend that check-equal?/values
could be used as a function so I made it a pure macro.
I don't see why it can't act as a function when used higher order, it would just lose its magical value-capturing abilities (and any "value-list" argument would be a list of one value)
@AlexKnauth I see what you mean now: if check-equal?
picked up the #%app
from the use site, then your examples and line of inquiry make sense to me.
But even if it did, I'd say that changing it is still a good idea. I don't think that examples like you posted are ones that we need to worry about.