Arc Forumnew | comments | leaders | submitlogin
2 points by akkartik 3527 days ago | link | parent

How would you represent multiple arguments? Or a variable number of them?


2 points by rocketnia 3527 days ago | link

"How would you represent multiple arguments?"

Right now I'm juggling two ways to pass multiple arguments.

For globally defined functions, such as the ones in the examples, I construct a data structure like (plus term) which holds all but one of the "arguments," and then I call it with the final argument.

For higher-order functions, the call site is using a function that was constructed elsewhere, so it has to use some other method to cram in more than one argument. I think my favorite approach right now is to define a data structure specifically to carry these arguments. This way if the program is refactored and the arguments change, the new code can still be backwards compatible with old stack snapshots that mention the old data structure. All it takes is for the new code to have a conditional where it handles both the old tag and the new one.

The second calling convention could work for global functions too, and it might be a lot less awkward for documentation; when I describe a two-argument function called (plus term) or a four-argument function called (my-func a b c), it looks pretty weird. :) Unfortunately, it really commits us to having messy stack snapshots full of macro-generated steps:

  Current return value: (one-args)
  Remaining stack:
  (one)
  (gs-12301)    \ will construct (negate-args _)
  (negate)
  (gs-12302 3)  \ will construct (plus-args 3 _)
  (plus)
  (gs-12303)    \ will construct (factorial-args _)
  (factorial)
  (gs-12304 3)  \ will construct (times-args 3 _)
  (times)
  (gs-12304 4)  \ will construct (times-args 4 _)
  (times)
  (gs-12304 5)  \ will construct (times-args 5 _)
  (times)
Hmm, maybe this isn't much worse than before. Factorial is a funny example because I can put all the function calls in the last argument, which makes the stack look nice. If I put them all in the first argument instead, we'd see some macro-generated save points:

  (defun factorial () n
    (times (call-new factorial () /plus (negate/one/nil) n) n))

  Current return value: (nil)
  Remaining stack:
  (one)
  (negate)
  (gs-12305 3)  \ will call (plus _ #suspended:n)
  (factorial)
  (gs-12306 3)  \ will call (times _ #suspended:n)
  (gs-12306 4)  \ will call (times _ #suspended:n)
  (gs-12306 5)  \ will call (times _ #suspended:n)
Real code is likely to have a handful of save points per function, so the stack will usually look something like that.

Oh. Actually, if I passed arguments in the form of data structures, that bizarro-factorial would look a little nicer on the stack. You can actually tell that it's going to call (plus) and (times) even if you don't look up the definitions of gensyms:

  Current return value: (one-args)
  Remaining stack:
  (one)
  (gs-12301)    \ will construct (negate-args _)
  (negate)
  (gs-12302 3)  \ will construct (plus-args _ #suspended:n)
  (plus)
  (gs-12303)    \ will construct (factorial-args _)
  (factorial)
  (gs-12304 3)  \ will construct (times-args _ #suspended:n)
  (times)
  (gs-12304 4)  \ will construct (times-args _ #suspended:n)
  (times)
  (gs-12304 5)  \ will construct (times-args _ #suspended:n)
  (times)
Okay, I might settle on using this calling convention for everything in this convenience layer. :) Thanks for prompting me to think about this.

I guess I'll probably use a slightly tweaked set of macros in future examples, taking this calling convention into account.

---

"Or a variable number of them?"

One option is to pass a cons list. Assuming there's a (list ...) macro, it's easy to call (foo ...) with a list:

  (foo/list ...)

-----

1 point by akkartik 3526 days ago | link

Oh so there actually is a constraint on the number of arguments you can pass in! Interesting.

-----