Arc Forumnew | comments | leaders | submitlogin
Syntax for optional/keyword args
4 points by applepie 5968 days ago | 12 comments
I feel CL's keyword arguments are a bit of a hack in that the arguments to a function are a plist, which must be parsed. Maybe would it be better for it to be a list of "argument objects", more like an alist?

Arc could borrow the keyword syntax from the unix command line arguments.

For example "--x y" could be read as "(argument 'x y)" and "--x" (if not followed by another s-expr) as "(argument 'x t)" or something similar.

Then

  (def cut (--seq "" --start 0 --end (len seq))
    ...)

  (cut "Hello World" 2 8)
  ===
  (cut "Hello World" --start 2 --end 8)
  ===
  (cut --end 8 --seq "Hello World" --start 2)
If functions/data could be overloaded by the names of the arguments, this could even work a bit like Smalltalk's messages, allowing "nice" things like:

(cut --help)



4 points by kennytilton 5968 days ago | link

CL keywords are a plist which must be parsed? Yeah, but (loosely speaking) by the compiler, not by me.* For example, in CL I can write your cut thus:

  (defun cut (seq &key (start 0) (end (length seq)))
     ;no parsing here
     (subseq seq start end))
...and then call it like this:

  (cut "Hello World" :start 2 :end 8)
(I made the seq parameter mandatory because I cannot understand why else I would be calling cut.)

keyword args make code more readable and as bogo said are extremely handy for those occasional functions with a kazillion parameters.

* One /can/ combine &rest all-args &key key1 key2 &allow-other-initargs and then parse the all-args as a plist if in a black-belt kinda mood, but it is exceedingly rare to find in normal code.

-----

2 points by are 5967 days ago | link

Arc currently supports optional parameters.

A CL-style keyword argument is like an optional argument in that you need to give it a default value for when it's omitted. But in addition, with a keyword argument at function-call-time, you can switch argument order if you specify the key.

This means that Arc could treat keyword arguments as a special case of optional arguments.

This is the optional-arg example from the Arc tutorial:

arc> (def greet (name (o punc (case name who #\? #\!)))

       (string "hello " name punc)) 
* redefining greet

#<procedure: greet>

arc> (greet 'who)

"hello who?"

But let's say you allow both "o" and "k" for optional arguments, and when there is a "k", you allow the argument symbol to be used as a key when calling the function, like this:

arc> (def greet (name (k punc (case name who #\? #\!)))

       (string "hello " name punc)) 
* redefining greet

#<procedure: greet>

arc> (greet 'who)

"hello who?"

arc> (greet 'john)

"hello john!"

arc> (greet :punc "?!?" 'jane)

"hello jane?!?"

Of course, once you allow an argument symbol to be used as an argument key when calling, you could actually extend this opportunity to every single argument, optional or not. Then you would no longer need the "k" syntax, since every argument is "keyable":

arc> (def greet (name (o punc (case name who #\? #\!)))

       (string "hello " name punc))
* redefining greet

#<procedure: greet>

arc> (greet 'who)

"hello who?"

arc> (greet :punc "?!?" :name 'jane)

"hello jane?!?"

-----

1 point by are 5967 days ago | link

And BTW, the :arg syntax for argument keys is only a CL convention. In Arc, it would be much more natural/readable to use arg: (colon appended rather than prepended to the symbol).

-----

1 point by AF 5966 days ago | link

Is that even a possibility? Doesn't : currently do function composition in Arc?

-----

1 point by are 5966 days ago | link

Wouldn't there be whitespace after an argument keyword, distinguishing it from function composition (no whitespace)?

-----

6 points by cooldude127 5968 days ago | link

it's a good idea, but i'm not sure i'm big on borrowing the unix syntax. it just looks kinda ugly and unlispy to me.

-----

2 points by nlavine 5967 days ago | link

Keyword arguments almost work with the current optional argument syntax, except for reordering:

  (def cut (seq (o ('start start)) (o ('end end)))
    ...)
And you call it like this:

  (cut seq (start x) (end y))
(destructuring with literal symbols - don't know if that's supported yet, but it should be.)

The only problem seems to be that you can't reorder them. What if you make the rule that you can have more than one name after the dot in an argument list, but they're unordered?

-----

1 point by aston 5968 days ago | link

Hmm. Allowing keyword args is pretty un-Arc-ish (is there a term for this yet?) in my eyes. Lots of extra tokens to specify keywords when you could've just used positional arguments with some optional args at the end.

-----

10 points by bogomipz 5968 days ago | link

Keyword arguments definitely have their place. They fix a problem with having too many optional args. Example:

  (def foo (a b c (o d) (o e) (o f) (o g))
If you want to call foo with g but not the other optional args, you'd have to write

  (foo 1 2 3 nil nil nil 42)
It's easy to loose track of positional args when there are more than say 5 of them (depends on the person), and it gets a lot worse when some are just nils. The above call with keyword args instead of optionals would be something like;

  (foo 1 2 3 'g 42)
Or, depending on the implementation, there might be a special syntax;

  (foo 1 2 3 :g 42)

-----

3 points by aston 5968 days ago | link

I think there are two distinct cases here. The first is when a procedure is taking a number of options/switches. The second is when the function has a long list of optional arguments.

I actually think the second is pretty rare, especially if the argument list is ordered by practical use. The first is handled pretty nicely by passing in association lists or hash tables. Of course, the current syntax for creating hash tables doesn't make using them as arguments super-easy, but they're the equivalent of passing a dictionary in python for kwargs, and they don't require any new syntax.

edit: In my imagined world, your last example would look something like

  (foo 1 2 3 (table g 42))
where table could take kv's ad infinitum.

-----

2 points by bogomipz 5967 days ago | link

I don't see the distinction between taking a number of options and having a number of optional arguments. To me, this is exactly the same. A switch is an argument that is only checked for true/false, which means that in ordinary function calls you only really want to bother with it when passing 't.

It's true that passing a data structure can do most of the job of keyword arguments. Any searchable structure will do, really. Perhaps the simplest form from the call site's point of view is a property list. If Scheme's plist-get was implemented in Arc (trivial, but pg would probably pick a shorter name):

  (def foo (a b c . rest)
    (plist-get rest 'g))

  arc> (foo 1 2 3 'g 42)
  42
So, what do keyword arguments give you that tables/alists/plists do not?

1) better self documentation - the function signature tells what keys it understands

2) less code in the function body - you access the value like any other argument

3) a default form which is only evaluated if no value was given - without adding code to the function body

-----

1 point by sjs 5967 days ago | link

How about something like this:

  (def kw args
    (listtab (pair args)))
That makes it pretty easy to pass around kw args, and with a macro you could make it a little more sugary by allowing unquoted keys.

-----