ruby picture

RCR 89: xx: yy as a shorter :xx => yy + m( x:, y:) keyword arg

Submitted by anonymous (Sun Apr 21 18:04:53 UTC 2002)

Abstract

This is a legacy RCR from Ruby Garden, submitted by anonymous. Matz has declared these RCRs obsolete, and asked that their authors resubmit them in the new format.

I would like

  xxx: yyy<br>

to be equivalent to

  :xxx => yyy.<br>


(click below to see a whole lot more explaination--Dave)

Problem

(RCR imported from old format)

Proposal

Example A: Call with named parameters. Instead of

  obj.do_something :some_arg => value1, :some_other_arg => value 2<br>

you would write:

  obj.do_something some_arg: value1, some_other_arg: value2<br>

Example B: Hash literal. Instead of

  a_hash = {<br>
    :if => "page 4",<br>
    :return => "page 5"<br>
  }<br>

you would write

  a_hash = {<br>
    if: "page 4"<br>
    return: "page 5"<br>
  }<br>

This syntax form is similar (with a whole different meaning) to the C construct where, instead of

  (*xxx).field             (in Ruby: :xxx => yyy)<br>

you can write

  xxx->field            (in Ruby?: xxx: yyy)<br>

I don't know if it should be restricted to symbol (i.e. must xxx be a symbol in xxx: yyy) or can it be extended to any expressions (i.e. (an_expression): yyy). Of course, this later one makes ":" become an operator, instead of a syntax sugar. And this has more impacts I guess (i.e. more work) on the Ruby parser.


Rationnal:

  1) xxx: yyy is shorter than :xxx => yyy<br>
  2) Promote the usage of symbols (efficient)<br>
  3) Compares well to similar constructs in some other languages that have either "named arguments" or "named values" (or both)<br>
  4) Natural, xxx: yyy is the syntax used in many internet protocols where the name of an attribute/field/element is followed by ":" and then the value of that attribute/field/element.<br>


Difficulties:

  I don't know for sure but there might be some conflicts with the "?:" ternary operator. Example: (a ? b:c) and (a ? b:c : d:e). However in this case I don't think that => would be considered valid either: (a ? :b => c : :d => e) but I may be wrong.<br>
  I don't know for sure what can follow ":" in :xxx today. Ideal any xxx that is valid in :xxx should be ok in the xxx: form too. <br>


Implementation:

  The implementation requires some look ahead (at lexical level), not much probably. i.e. What may first appear has a reference to a local variable xxx or a function xxx turns out to be the xxx symbol (when ":" is encountered). When that happens, "=>" should be pushed back in the input stream (that later one is easy I guess).<br>
  The implementation could focus on the simple cases first, when xxx is an identifier, and address more complex cases (when xxx is something more complex then an identifier, an arbitrary expression) later.<br>


BTW: "foo", y: "bar" could also be a nice shorter [:x,"foo", :y,"bar"]. I think it is a common idiom in Lisp to have such lists with flat pairs of symbol + value. Some other languages have a "name" attached to every values.


keyword arguments and xxx: yyy shorter notation for :xxx => yyy work together well =>


Considering the fact that methods callable with keyword arguments are yet to be developed / something of the future (existing methods where not designed to be called this way), there is little pain in asking the developer to tag the arguments that are keyword arguments.


Today (without keyword arguments):


  def drawline( x = O, y = 0, thick = false)<br>
     xxx<br>
  end<br>


Tomorrow with keyword argument (my proposal)


  def drawline( x: 0, y: 0, thick: false )<br>
    xxx<br>
  end<br>


Rationnal for tagging keyword arguments using ":" postfix:

  1) The look of the def is similar to the one of a call.<br>
    obj.drawline( x: 45, y: 55, thick: true) # call drawline method<br>
  2) Specifying a default value makes more sense for keyword arguments than it does for unamed ones. Hence A) the "=" can be avoided, B) the bad looking form def m( x:, y:) where x and y have no default values will be rare (btw: it would mean that the argument is mandatory, not optional ; for aesthetic reasons an optional "x: mandatory" or "x: required" sugar may be permitted).<br>
  3) Having some ":" inside the formal argument list makes it possible for the interpreter to know that a method expects keyword arguments. For such a method there is less need for the convenient hash object built when extra arguments are provided at call time. But such a convenient construction is still relatively easy to implement: built the hash with the values that remains after the first nth values, where nth matches the number of arguments (minus one, the one that will hold the hash). ex:<br>


  def m( x: 0, y: 0, *args ) xxx end<br>
  obj.m( 2, 4, foo: "hello", bar: "world") # positional, not keyworded<br>
  obj.m( x: 2, y: 4, foo: "hello", bar: "world") # args gets foo &amp; bar<br>
  obj.m( x: 2, y: 4) # args gets empty hash<br>
  obj.m( x: 2) # y gets 0, args gets empty hash<br>
  obj.m( y: 4) # x gets 0, args gets empty hash<br>
  obj.m( foo: "hello", bar: "world")<br>
    => exception, bad parameter, <br>
expecting x:, y:.

  In this last case, the interpreter could potentially do a better job and figure out that x: &amp; y: should get their default value. It would have to build the hash first and then unshift the first elements of the hash *if* their key matches the name of one of the keyword arguments (pretty inefficient I'm afraid)<br>

  obj.m( bar: "world", foo: "hello", x: 0)<br>
    => exception, bad parameter, expecting x:, y:.<br>

  In this case the interpreter might still figure out the intend of the caller. However I would question how much readable/contrived this is. That keyword arguments should appear before hash entries seems a rather reasonable constraint.<br>

Nota: if xxx: yyy is to become equivalent to :xxx => yyy then the previous examples would be valid using the current :xxx => yyy form too:

  obj.m( 2, 4, :foo => "hello", :bar => "world")<br>
  obj.m( :x => 2, :y => 4, :foo => "hello", :bar => "world")<br>
  obj.m( :x => 2, :y => 4)<br>
  obj.m( :x => 2)<br>
  obj.m( :y => 4).<br>

Comparing the two, I certainly prefer the former versions (with xxx: yyy) but I guess that seasoned Ruby/Perl developers are used to the later versions (with :xxx => yyy). Yet, one version is shorter/clearer than the other one (to fresh innocent eyes, at least mine).


There is no way a call with keyword arguments can be as efficient as a call with "positional" arguments. As a result, the language should make it easier to use positional arguments in most cases. keywords arguments should be used mostly when there are more than one optional argument. Else you design a language that promotes the use of inefficient constructs and this may have bad impacts on the success of such a language (that would be considered "slow" by people who don't known how much speed they are trading to get more flexibility and consequently make the wrong tradeoffs).


Jean-Hugues
jean_hugues_robert@yahoo.com

Analysis

(RCR imported from old format)

Implementation

(RCR imported from old format)
ruby picture
Comments Current voting

Why no comments? (Dave, 2002-04-28 08:53:31)

This strikes me as a very interesting idea. It isn't so much a saving in characters types as a change to the character of the argument list. fred(:p1 => a, :p2 => b) is a clever hack, but it looks like you're passing a hash to the function. fred(p1: a, p2: b) looks more like keyword arguments.

Now I know that real keywords may be coming along at some point, and the need to create hashes to handle this kind of thing will evaporate, but in the mean time this seems to be a nice idea. Comments?

b/c Jean-Hugues was very complete... (, 2002-05-08 20:38:10)

I think Jean-Hugues did such a great job explaining his idea, its benefits, and its tradeoffs that there wasn't much else to say except vote for it :-)

But (, 2002-06-16 15:18:19)

Why not allow positional arguments be used as keyword arguments?


def fun(xxx, yyy)
end
fun(xxx: 0, yyy: 1)

?


Strongly opposed 0
Opposed 0
Neutral 0
In favor 0
Strongly advocate 0
ruby picture
If you have registered at RCRchive, you may now sign in below. If you have not registered, you may sign up for a username and password. Registering enables you to submit new RCRs, and vote and leave comments on existing RCRs.
Your username:
Your password:

ruby picture

Powered by .