I still don't see how you're advocating a solution of anything other than call-with-values under a different name.
I don't know Racket itself, so let me switch to common lisp which has the same "problem"... except it comes with a super-simple macro to do exactly what you want, apparently:
(multiple-value-bind (quotient remainder) (floor x y)
;; some code referring to quotient and remainder
)
which (excepting perhaps edge-cases I'm not considering, and definitely excepting some error-checking) is just
The same thing should work in Racket, modulo syntax and names.
Asking a lisp programmer to do everything with special forms/primitive language elements is like asking a Haskell programmer to do everything in the IO monad. Sure, it's possible, it will work just fine; but... it's just wrong.
By the way, the existence of the #'values function by no means implies there's some data structure being created; indeed, that's the whole point of the #'values construct. Anything you could do with #'values you could do in a list which you then destructured; #'values is to be used when the common-case is to throw away the "structure" and just use each value individually or not at all, like with the return values of #'floor. So in fact #'values is exactly what you want; I'm not sure why it's a "hack" when lisp programmers write it under one name and "the solution" when you write it under a different name.
Ah, it occurs to me that maybe racket doesn't work like this, but in common lisp at least, the transiency of values can be seen in that when you say, e.g., (+ (floor a b) c), the remainder of a/b is thrown away; just the first value is used. That's another huge benefit over structures-to-be-destructured, but obviously doesn't work if the extra values are always important.
The point of my discussion with Maybe is that /even if/ it's just a nullable pointer, conceptually it's essentially a [restricted] tuple. (pointer-is-null,data). There's still two pieces of data there, even if the representation of one of them is cleverly folded into generic object representation or what-have-you. By definition it can't be done with a single piece of data: (Maybe a) is not the same type as (a), for all a. Even if (Maybe Word16), e.g., were represented in a single value at a single place in memory, that value would have to hold more than 16 bits. Then it's viewable as a... tuple, again; one element in 16 bits and the other in the remaining bits.
I don't know Racket itself, so let me switch to common lisp which has the same "problem"... except it comes with a super-simple macro to do exactly what you want, apparently:
(multiple-value-bind (quotient remainder) (floor x y) ;; some code referring to quotient and remainder )
which (excepting perhaps edge-cases I'm not considering, and definitely excepting some error-checking) is just
(defmacro mvb (vars form . body) `(multiple-value-call (lambda ,vars ,body) ,form)
The same thing should work in Racket, modulo syntax and names.
Asking a lisp programmer to do everything with special forms/primitive language elements is like asking a Haskell programmer to do everything in the IO monad. Sure, it's possible, it will work just fine; but... it's just wrong.
By the way, the existence of the #'values function by no means implies there's some data structure being created; indeed, that's the whole point of the #'values construct. Anything you could do with #'values you could do in a list which you then destructured; #'values is to be used when the common-case is to throw away the "structure" and just use each value individually or not at all, like with the return values of #'floor. So in fact #'values is exactly what you want; I'm not sure why it's a "hack" when lisp programmers write it under one name and "the solution" when you write it under a different name.
Ah, it occurs to me that maybe racket doesn't work like this, but in common lisp at least, the transiency of values can be seen in that when you say, e.g., (+ (floor a b) c), the remainder of a/b is thrown away; just the first value is used. That's another huge benefit over structures-to-be-destructured, but obviously doesn't work if the extra values are always important.
The point of my discussion with Maybe is that /even if/ it's just a nullable pointer, conceptually it's essentially a [restricted] tuple. (pointer-is-null,data). There's still two pieces of data there, even if the representation of one of them is cleverly folded into generic object representation or what-have-you. By definition it can't be done with a single piece of data: (Maybe a) is not the same type as (a), for all a. Even if (Maybe Word16), e.g., were represented in a single value at a single place in memory, that value would have to hold more than 16 bits. Then it's viewable as a... tuple, again; one element in 16 bits and the other in the remaining bits.