[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