My understanding of this issue is clearer. But see comments below
Christian Jaeger wrote:
Arthur Smyles wrote:
I've looked into your bug comment.
The additional use case that I see is your example code:
(let ((v (values 1 2))) (call-with-values (lambda () v) cons))
It could be re-written as:
(let ((v '(1 2))) (call-with-values (lambda () (apply values v)) cons))
I don't see what a user-visible values structure provides when the user already has cons, vectors, and records.
I don't understand your point.
My point was that the (values 1 2) would be from user code.
Example (and yes macros don't even have to be involved):
;; library code:
(define (db-transaction db thunk) (db-begin! db) (with-exception-catcher (lambda (e) (db-rollback! db) (raise e)) (lambda () (let ((res (thunk))) (db-commit! db) res))))
;; user code:
(define (dosomequery-with-x x) (db-transaction mydb (lambda () (let ((a (db-query mydb "foo" x)) (b (db-query mydb "bar" x))) (values a b)))))
(call-with-values (dosomequery-with-x 1234) (lambda (a b) ...))
;; or
(let-values ((a b (dosomequery-with-x 1234))) ...)
The point was that the (values 1 2) would be provided by the user of the library. And that the user doesn't know about the implementation of the library. But be able to pass multiple values through the layer that the library might have (again, the user wouldn't necessarily be able to know whether the library imposes an additional layer between the continuation of the callback code and the continuation of the library call). And the library author needn't bother about multiple values either.
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. 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))
If the library wanted to be multi-value aware, 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.
If I only wanted value a the user's code simplifies to
(dosomequery-with-x 1234)
In this example, if Gambit were to work as you suggest, the let-values couldn't get at the b value.
I see the call-with-values as a way to create multi-value continuations and values as the standard way to call them. If the continuation does not accept multiple values (any continuation not created by call-with-values) then I'd like to see it behave as if you just returned one value.
I guess my and Brad's suggestions are to specify that at the call site explicitely.
I am suggesting that too. It doesn't work that way though. My original example
(if (values #f #f) #t #f)
doesn't work in scheme because values creates a special object that is not false so to make it work properly, I have to do:
(if (call-with-values (lambda () (values #f #f)) (lambda (primary . secondary) primary)) #t #f)
But if I always have to do that in order to use my function, then I might as well not use values/call-with-values and split the function into 2.
Unfortunately, the only way to use the current implementation of 'values' properly is to de-structure it using call-with-values. I think that is redundant since I can perform the same with a list, vector, or record.
As I said, and as has discussed many times, yes, multiple-values is kind of redundant.
The idea has always been that since there are multiple-value function entries, there should also be (as a symmetry) multiple-values function exits.
Now whether the current ways multiple values are being implemented solve that well is an open question.
That's what I'm asking and answering :D
Manual continuation passing style is the best pre-existing and probably still best current way for passing multiple "return" values; then you can give (lambda (a . ignore) ...) as the continuation if you don't want to look at the remaining values.
Christian.
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))
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.
So my take away from the discussions so far are:
If your scheme implementation does not support multi-value continuations then don't bother with values/call-with-values. They are basically standardized hooks for implementations that do support multi-value continuations.
Arthur