[gambit-list] strange results using values
Arthur Smyles
atsmyles at earthlink.net
Mon Sep 22 19:25:47 EDT 2008
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
More information about the Gambit-list
mailing list