[racket] Splicing `values' in-place

From: Eli Barzilay (eli at barzilay.org)
Date: Sat Jul 13 11:31:38 EDT 2013

Two days ago, Matthias Felleisen wrote:
> 
> Your uses of values are covered in apply/map/append/list
> trickeries. Using values might be more elegant, but yes, it's
> currently not possible.

Not possible, but as usual, easy to play with (if you're willing to
pay the runtime overhead).  Since it's a semi-popular subject that
comes up frequently enough, at some point I made it into a "language
one-liner" thing -- it's a cute exercise but unfortunately multiple
values are already an "exotic" enough feature to make it into a good
enough first-language thing...

You start with a quick macro as a first step (I haven't looked, but
it's probably close to what Neil posted):

  #lang racket
  (define-syntax-rule (vals expr ...)
    (append (call-with-values (lambda () expr) list) ...))
  (define-syntax-rule (app f expr ...)
    (let ([l (vals f expr ...)]) (apply (car l) (cdr l))))

The next step is to rename things so you can use it implicitly in the
rest of the code for all function applications

  #lang racket
  (require (only-in racket [#%app r:app]))
  (define-syntax-rule (vals expr ...)
    (r:app append (r:app call-with-values (lambda () expr) list) ...))
  (define-syntax-rule (#%app f expr ...)
    (let ([l (vals f expr ...)]) (r:app apply (r:app car l) (r:app cdr l))))
  ;;
  (define (foo) (values list-ref '(x y z)))
  ((foo) 1) ; => 'y

but then you take it in the other direction, and make it into a
language instead, first do it with sub-modules:

  #lang racket
  (module values racket
    (define-syntax-rule (vals expr ...)
      (append (call-with-values (lambda () expr) list) ...))
    (define-syntax-rule (app f expr ...)
      (let ([l (vals f expr ...)]) (apply (car l) (cdr l))))
    (provide (rename-out [app #%app]) (except-out (all-from-out racket) #%app)))
  (require 'values)
  (define (foo) (values list-ref '(x y z)))
  ((foo) 1) ; => 'y

And finally just remove the sub-module wrapper and make it into a
"proper" language.

(BTW, for people on IRC: one feature of rudybot is that you can
initialize it from a URL holding some code -- so it's probably still
possible to use "init http://tmp.barzilay.org/values.rkt" to play with
this.  (Yeah, just confirmed that it still works.))


Two days ago, Matthew Flatt wrote:
> To elaborate on "currently not possible" (because this idea shows up
> from time to time), allowing splicing of results in function-call
> subexpressions would break equivalences that are currently exploited
> by macros and the compiler.

IMO, it's a bad idea for a more basic problem: you get functions to
interact with each other based on where they appear in the code.  The
above is an extreme example of an application with three different
types, so if you have:

  (define (foo) (values list-ref '(x y z)))
  (define (bar) 1)
  ((foo) (bar))

and you decide to return just the function from `foo', then you need
to adjust `bar' too.  This means that changes in return arity are very
expensive since you need to track down all caller sites and revise
them.  (You need to do that now too -- but if you forget something
you'd almost always get an error instead of random-and-possibly-buggy-
behavior.)

-- 
          ((lambda (x) (x x)) (lambda (x) (x x)))          Eli Barzilay:
                    http://barzilay.org/                   Maze is Life!

Posted on the users mailing list.