Arc Forumnew | comments | leaders | submitlogin
3 points by akkartik 3342 days ago | link | parent

I have some new suggestions:

a) Quote optional args. (Thanks waterhouse)

  (a b (c d)) ; destructured
  (a b '(c 3) 'd) ; optional args
The quotes are only required to resolve ambiguity.

  (a b (c 3) (d nil)) ; optional args - defaults aren't variables

  (a b '(c A) '(d) ; needs quote to disambiguate from destructuring
b) Quote destructured args to distinguish them from optional args.

  (a b '(c d)) ; destructured
  (a b (c 3) (d)) ; optional args
Unlike the previous case you'll never be able to drop the quotes. But you'll rarely need more than one.

c) Use dot to delimit optional args from required ones. So arg lists could now have 3 different zones: required/destructured args, optional, and rest/body args.

  (a b . c d) ; optional args since you can only have one rest arg
  (a b . c) ; rest args
  (a b . (c)) ; c is optional - rest args will never be destructured.
  (a b . (c 3) (d 4) . e) ; optional args with defaults and a rest arg.
The dot delimiter for optional args is only needed for disambiguation, so this is equally clear:

  (a b (c 3) (d 4) . e) ; defaults aren't variables
But this requires the dot:

  (a b . (c A) . e) ; A is bound in the environment

What do y'all think of these options?

4 points by conanite 3342 days ago | link

(a) might look funny when you want to evaluate an expression for the default value of an optional arg

  (def foo (a b '(c (defaults 'c x y z)) ...
To the untrained eye, (defaults 'c x y z) looks like it should not be evaluated because it's quoted

(c) makes parsing harder ... the assumption of only one element after the dot may be built into the parser

  arc> '(a b c . d e)
  Error: "UNKNOWN::8: read: illegal use of `.'"
It could be some privileged symbol instead of "." though ...


1 point by akkartik 3342 days ago | link

Great points. I realized c was breaking the metaphor of '.'; I didn't realize it would actually refuse to parse.

It doesn't make sense to quote forms that may have expressions to evaluate, so b is better than a.


2 points by fallintothis 3342 days ago | link

I dunno. It seems I'm in the minority, but I actually like using (o arg default). Only problem is the whole "don't use o when destructuring" thing, which is annoying. As for these options, I agree with conanite. In general, overloading either quotes or dots would be weird.

You could tweak o to be nicer to multiple parameters, though. E.g.,

  (def trim (s (o where 'both) (o test whitec))
could instead be

  (def trim (s (o where 'both test whitec))
Not that it's much of an issue:

  arc> (def optional (parm) (caris parm 'o))
  #<procedure: optional>
  arc> (ratio (andf alist ~dotted [some optional _]) (vals sig))
  arc> (ratio (andf alist ~dotted [>= (count optional _) 2]) (vals sig))
  arc> (apply max (trues (andf alist ~dotted [count optional _]) (vals sig)))


2 points by akkartik 3342 days ago | link

If CL's &optional came to this forum it would say: If you're going to have a new keyword like o, at least take advantage of the fact that required args can't follow optional args, and get rid of the parens.

I like how you argue this is a rare case :) My motivation is purely aesthetic, under the assumption that I'll want to make a long term commitment to a 100-year language something like arc. I'm trying to add keyword arguments to arc, and I figure I might as well revisit this decision at the same time.


3 points by conanite 3342 days ago | link

Two problems with (o arg default) - you need to remember not to use 'o at the start of a destructuring list (and not get confused when you see (def handle-request ((i o ip)) ...) ), and as akkartik says it's paren-inefficient, a single keyword to delimit required/optional args would mean fewer tokens.

The first problem is easy to fix though - use a symbol that's less likely to be an arg name to identify optional args. How about '= ?

  (def myfun (a b (= c (something)) ...)
it has the advantage of similarity with ruby:

  def myfun a, b, c=something
disadvantage: looks silly when you don't supply a default value:

  (def myfun (a b (= c) ...)


2 points by fallintothis 3342 days ago | link

get rid of the parens

Thinking about the paired-o suggestion I made, it has an advantage over grouping single optional arguments by their own parens -- at least if you have more than 2 optional parameters :). Compare:

  (a b (opt-c default-c))
  (a b (o opt-c default-c))

  (a b (opt-c default-c) (opt-d default-d))
  (a b (o opt-c default-c opt-d default-d))

  (a b (opt-c default-c) (opt-d default-d) (opt-e default-e))
  (a b (o opt-c default-c opt-d default-d opt-e default-e))
It's like a 3-character &optional, just with one of the characters at the end. However, you'd need to specify nil defaults by hand (though you might let an uneven pair at the end default to nil). E.g.,

  (def markdown (s (o maxurl) (o nolinks))
could, at best, become

  (def markdown (s (o maxurl nil nolinks))
But paired-o also generalizes the current behavior, so we could still use the original signature.

Just food for thought.


1 point by akkartik 3341 days ago | link

Yeah I'd thought of that and forgotten about it :) Thanks.


1 point by akkartik 3341 days ago | link

BTW, could you tell me how common destructured args are in the arc codebase, just for comparison?


3 points by fallintothis 3341 days ago | link

In def and mac parameter lists, they're pretty much never used.

  arc> (def destructuring (parm) (and (acons parm) (isnt (car parm) 'o)))
  #<procedure: destructuring>
  arc> (ratio [errsafe:some destructuring _] (vals sig))
  arc> (keep [errsafe:some destructuring _] (vals sig))
  (((y m d)) ((y m d)))
They're most often used implicitly with let.

  $ grep "(let (" *.arc | grep -v "(let (unquote"
  app.arc:      (let (f url) afterward
  app.arc:                        (let (typ id val sho mod) it
  app.arc:  (let (nums (o label "")) (halve s letter)
  app.arc:         (let (ms ds ys) toks
  arc.arc:      (let (vars prev setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:        (let (binds val setter) (setforms place)
  arc.arc:        (let (binds val setter) (setforms place)
  arc.arc:    (let (binds val setter) (setforms place)
  arc.arc:  (let (y m d) (date s)
  arc.arc:  (let (binds val setter) (setforms place)
  html.arc:      (let ((opt val) . rest) options
  news.arc:             (let (t1 t2 t3 . rest) toks
  srv.arc:  (let (i o ip) (socket-accept s)
  srv.arc:                (let (type op args n cooks) (parseheader (rev lines))
  srv.arc:  (let (type op args) (parseurl (car lines))
  srv.arc:  (let (type url) (tokens s)
  srv.arc:    (let (base args) (tokens url #\?)
  srv.arc:      (let (kill keep) (split (rev fnids*) nharvest)