(Before replying let me remind ourselves that there are two things in this thread:
(1) whether the semantics of Gambit or that of dropping superfluous values is the better one. Or at least making each of us understand each other's points. (And I think, I do see the point that you'd like to have that comfort of just ignoring some less-important values that you might not always want, this is easy to see. And you'd like not having to specify anything syntactically on the receiver side to get this. I do question the merit of this versus the problems of it, though.)
(2) whether there is a good way unifying both approaches in one and the same system, with hopefully workable integration of code of both philosophies.
Let me state that I'm a bit unclear which of those you are targetting below at some places.)
Arthur Smyles wrote:
My understanding of this issue is clearer. But see comments below ... This only works because Gambit doesn't return multiple values. As Marc mentioned earlier, (values 1 2) is currently implemented as a vector with a special type.
(The implementation doesn't really matter (they could also be stored in global variables if the implementation wouldn't have to deal with call/cc or threads, or the vector could be optimized away if the consumer is the direct continuation of the producer); the fact how they are being implemented will just make clear how they behave. The point is that the multiple values are being tunneled through continuations which are not "prepared" (so to speak) of taking multiple values, until they reach a continuation which is.)
So you can achieve the same thing using the user code:
(define (dosomequery-with-x x) (db-transaction mydb (lambda ()
(let ((a (db-query mydb "foo" x)) (b (db-query mydb "bar" x))) (list a b)))))
(apply (lambda (a b) ...) (dosomequery-with-x 1234))
Yes, I already said "As I said, and as has [been] discussed many times, yes, multiple-values is kind of redundant" (not sure why you're bringing this up again, but I guess you're looking after that additional benefit they would provide you).
If the library wanted to be multi-value aware,
My point was that as library author I *don't want* to remember having to make everything multi-value aware.
(It's like not casting a data type in stone: I don't want to cast in stone how many values are being passed through a function of mine by the user of that function.)
then it could be re-written as:
(define (db-transaction db thunk) (db-begin! db) (with-exception-catcher (lambda (e) (db-rollback! db) (raise e)) (call-with-values thunk (lambda vals (db-commit! db) (apply values vals)))))
This makes sense since only call-with-values will create a multi-value continuation. call/cc by default only creates single-value continuations.
(call/cc makes continuations first class, it doesn't create them [well, it may create a continuation *object* representing the continuation]. A continuation captured by call/cc will expect exactly as many values as the continuation of the call/cc call takes (because it's the same).)
(What do you mean when you say "This makes sense"?)
Are you suggesting that I should really write my code as you have shown above? If so, I would have thought that it shows quite clearly why the approach of dropping values is flawed. You don't think so?
Tunneling multiple values without having to change the code makes "normal" Scheme code automatically compatible with multiple values, that's the good thing about the tuples/Gambit approach.
... I think you've given me an idea as a workaround to simulate what I want
(define (mv-fun . k) (if (pair? k) ((car k) #f #f) #f))
I don't understand. What would you pass to mv-fun?
In which case if I want the multiple-values I have to explicitly pass the continuation. I'd rather have the scheme implementation hide this stuff with values/call-with-values then have this kind of plumbing in apis that I'd develop.
I'd rather specify places where I expect multiple values explicitely than having to change all code which would *possibly* need to deal with a user expecting to be able to pass multiple values through. Since the former are few, whereas the latter could be *every* function.
Am I missing something?
So my take away from the discussions so far are:
I have big difficulties understanding the following paragraph.
If your scheme implementation does not support multi-value continuations
What is your definition of a scheme implementation which does support multi-value continuations?
In the R5RS sense, Gambit is one, as are the implementations which drop values. All of them do support multi-value continuations -- continuations which are created by call-with-values.
The difference is whether *other* continuations ("normal" ones) are behaving like (lambda (value . ignored) ..) including always dissecting the values immediately in the continuation the values are being passed to, or (lambda (value) ..) *without* dissection.
So (my guess) you are counting Gambit into the camp which "does not support multi-value continuations"?
then don't bother with values/call-with-values.
Why not?
They are basically standardized hooks
Why "hooks"?
for implementations that do support multi-value continuations.
What do you want to say? "Since |values| does not offer new features over |list| in an implementation like Gambit, don't bother using it there", or something else?
Christian.