Arc Forumnew | comments | leaders | submitlogin
Multiple Return Values?
6 points by Bogart 6025 days ago | 11 comments
Does arc have multiple return values like the "values" function allows in scheme? I tried defining it with (def values things (ccc [apply _ things])) but it didn't work.


7 points by almkglor 6025 days ago | link

The canonical Arc way of doing it is by returning a list. Then you can deconstruct it with 'let:

  (def sum-and-diff (a b)
    (list (+ a b) (- a b)))

  (let (sum diff) (sum-and-diff foo bar)
    (do-something-on sum)
    (do-another-thing-on diff))

-----

3 points by bOR_ 6025 days ago | link

I picked that habit up from some of the examples in the forum, and it works for me. Is there a reason why one would really want multiple return values?

  (= abba (list 1 12))

  (let (x _) abba
     (prn x))

-----

6 points by absz 6025 days ago | link

You can also use nil or t as your "ignored variable", instead of _; they won't even be rebound. E.g.

  (let (a nil nil d) (range 1 4)
    (list a d))
produces the list (1 4).

-----

3 points by eds 6025 days ago | link

The one nice thing about multiple values that I don't think returning lists accomplishes is that if you have a function that doesn't expect to receive multiple values, it will just use the first value returned. For example, in CL, #'truncate returns two values, the quotient and the remainder. But if you pass the return values of #'truncate to #'+, it just pretends you only passed a single value.

  * (truncate 5 3)
  1
  2
  * (+ (truncate 5 3) 6)
  7
I don't know of any way to make this work implicitly with returning lists... you would need to explicitly test if you were receiving a list and then destructure it accordingly. (Please correct me if you know of a better way around this.)

-----

5 points by almkglor 6024 days ago | link

Using (car:truncate ...) is th best I could think of ^^

-----

2 points by bOR_ 6023 days ago | link

that's pretty close to perfect.

Anyway, I suppose that the programmer knows what the output is of a certain function, and not be surprised if a function churns out a list rather than two values ;).

-----

3 points by bogomipz 6020 days ago | link

What if your function originally just returned one value, but at some later point you realize that a second value would be useful in some situations?

With multiple return values you can just extend it without breaking existing clients. If, on the other hand, you add a list wrapper around the returned values, all call sites must be changed to take car of the list.

-----

3 points by bOR_ 6014 days ago | link

That would be useful indeed. The flip side of the coin might be something that was sort of mentioned in 'on lisp'. If all functions return only one value (be it a list or a single value) by default, you can write a general memoize layer around functions that doesn't have to check how many multiple return values are returned.

I also noticed a carif function in arc. If you are worried about single values that will become lists in the future, you might start using carif in your current clients.

-----

2 points by dreish 6024 days ago | link

Returning multiple values puts multiple non-GC'd values on the stack. This is fast, but capturing and using those values is somewhat inconvenient for the programmer. Returning a cons creates one other cons in the heap that will need to be GC'd for each additional value returned.

-----

1 point by almkglor 6024 days ago | link

In theory we could do this with arc2c. However, I'm not 100% sure this is necessary with a "really good" optimizing compiler.

We could defer destructuring of arguments in arc2c to as late as possible, so that we could do some amount of optimizing away 'cons cells when the cells themselves are used only for returning multiple values. Which is arguably difficult, since each stage in arc2c expects arguments to be in undestructured form, i.e. (let (foo bar) niaw ...) => (let tmp niaw (with (foo (car tmp) bar (cadr tmp)))). If arguments are kept in non-destructured form, we would need to modify the way that function parameters are stored (to handle (o foo (expression))) and check each stage in the compiler.

Edit: When optimizing raymyers' treeparse, I actually transformed parsers to CPS form in order to return multiple values without all the construction and deconstruction of 'cons cells, which helped reduce some of the overhead.

-----

2 points by absz 6025 days ago | link

No, it does not. Why do you want it? Perhaps there's a better (or at least more Arcish) way to accomplish it. Returning a list, for instance. To quote arc.arc:

  ; The problem with returning a list instead of multiple values is that
  ; you can't act as if the fn didn't return multiple vals in cases where
  ; you only want the first.  Not a big problem.
Also, a formatting tip: to get your text to appear as code, surround it by blank lines and indent it by two spaces.

-----