Named Parameters? Aren't They All Named? #
None of us wants to delve back into the block/method/param argument again, right? But we could still use a good answer. Dan Berger has an explanation of how Sydney got named parameters without need to plunder Ruby’s current method definitions.
# A standard Ruby method declaration def foo(a, b, c=4) ... end # Ideal syntax foo(a:1, b:2, c:3) # a=1, b=2, c=3 foo(a:1, b:2) # a=1, b=2, c=4 foo(1, 2, c:3) # a=1, b=2, c=3 foo(1, 2, 3) # a=1, b=2, c=3
If you’d like to give Sydney a try, scan for tarballs on Evan’s blog. (Spotted on Late to the Party.)
rick
That’s exactly what I thought. I was going to ask but I figured someone would just tell me to read ruby-talk.
TG
eww. Someone tell Perl Ruby’s catching up!
Dave Burt
foo(b:2, a:1) # a=1, b=2, c=4
And do all of these work?
foo(1, module_var::ConstantName)
foo(1, b: :two)
foo(1, ::ConstantName)
foo(1, 2, b: :two)
foo(1, b: x ? 2 : :two)
Daniel Berger
Dave, yes, you could do foo(b:2, a:1) and it would work.
Your second case, foo(1, mod::Const), would work like a regular positional parameter, but that’s a good test case to add. Same for foo(1, ::Const) example. Again a good test case.
The foo(1, 2, b: :two) example would fail. There cannot be a space between the keyword, the colon and the value. Hm…I wonder how it should handle symbols then. That will look strange.
I suspect the last example will fail, though I’ve never used conditional logic like that for a parameter. I’ll ask Evan. :)
Merc
Hmm, that’s an explanation of why Sydney got keyword parameters, but not how it was done. I do really like the syntax though.
If you want to use the variable names, use them, if you don’t, don’t. There are no special keyword parameters.
Matz
Two issues:
Proposed keyword arguments are not that complex once you understands the whole picture.
- matz.
Daniel Berger
Matz, as to your first point, I’ll have to talk to Evan about that. If it takes extra work to create named parameters in C extensions, then so be it. Something like a “define_named_parameters()” function perhaps, but whatever. The onus will be on the C extension developers.
Regarding the second point, I’m not sure where you’re going. If the interface isn’t public it’s a non issue because no one is going to use it anyway. As for proper names, well, I think being forced to use proper names is a good thing.
Now, when I say that, I have the stdlib and 3rd party stuff in mind, not the core. One potential option to avoid the “keywords run amok” scenario that you envision is to make keyword arguments something that must be explicitly enabled via Evan’s “Behaviors” implementation.
In fact, that’s how it works now, but Evan was going to make that the default behavior.
Daniel Berger
Dave, correction, a space is allowed after the colon. So, your symbol example should work. I suspect the ternary operation might require an extra set of parens, but we’ll see.
Tsela
Daniel, matz’s second point wasn’t about the entire method not being public, but the fact that the actual names of the arguments are not part of the interface of a public method. They are implementation details that are hidden from the user, and rightly so (they are basically local variables to the method. The user shouldn’t need to know about their names).
When one uses keyword arguments, one is basically breaking the premise that implementation details are hidden. Suddenly implementation details (the names of the arguments) become part of the interface. So it makes sense that this case should be the marked one, thus that keywords arguments should explicitly be marked that way in the definition of the method, to insist that they are suddenly part of its public interface.
Patrick May
All arguments could be returned in a single ordered hash/array:
Matz
Patrick, it is possible, but I’m afraid it slows down (already slow) method invocation even worse.
hgs
Patrick May: Did I ever mention that I’m still envious of Lua’s tables, and want them in Ruby? (grin)
Daniel Berger: Could we have named params without default values, like:def foo(bar:, baz:); do_stuff_with(bar,baz); end
?(I don’t understand the colo(u)r rules for paragraphs :), off-topic-ly.)
Daniel Berger
hgs, I’m not sure without asking. I’m also not sure what you want to happen in that case. Seems a pointless thing to do.
Patrick May
Matz: Fair enough. What I’m curious about is, what is necessary to delegate a method?
currently: future?Patrick May
hgs: Lua looks interesting, I’m checking it out.
hgs
Daniel Berger:
def foo(a:, b:)...
(with more params) would allow people who wished to use the names to order the parameters differently. I’ve been using Ruby since about 2000 but I’ve always had to lookup which way round alias works. I’ve got it now, but to be able to doalias new: :bar, old: :foo
would be less confusing for me because I always want to read it as “alias that to this_new_name”, which is actually backwards.Daniel Berger
hgs, oh, I think I misread your post as a method call, not a declaration. Like I said, you won’t have to do anything special in the method declaration. Thus “def foo(a, b)” can be called as “foo(b:1, a:2)” if you want.
And yes, with the current Sydney syntax, you can do exactly what you want wrt alias. :)
murphy
@Patrick, @matz: I also thought about delegation of all parameters, whether normal, multiple, named or block. It would get really verbose if you’d have to give four different variables just for delegating; and it’s bad for performance either, I think.
Ruby already has a cool syntax for “any parameters allowed”, it’s
def foo(*)
.So what about making this available for method calls, too:
Is that possible? Or is there another solution?
Matz
Since all keyword arguments will be packed in a hash at the end of ordinal argument list, delegation should be done by plain
even after introducing keyword arguments. That’s one of the design goal of my keyword argument design.
**keys
I don’t mind Matz’s proposal. In fact I like it a lot. You guys don’t give anything a try it seems. There are plenty of good points about it:
I think pushing for the simple Python style model will be passing up an oportunity to allow for things that can’t be done otherwise. Play around with some psuedo code and see what I mean.
You guys are all whining and it is not that big of a deal. Give Matz a break at least…
trans
Matz, then todays hash elements would be index -2? Or are named arguments and ending hash parameters mixed?
or
Trejkaz
Mixing named arguments and ending hash parameters might make sense. For instance, if Rails switched from using hashes to named parameters, it would be rather painless if the two were the same.
Matz
Trans, you can’t mix in-line hash and keyword arguments, so that
would cause an error. But both
and
works, and args becomes [{:a=>1,:b=>2}]
Patrick May
Matz: Thanks for the answers! I think I understand… :-)
riffraff
I really like the new proposal, I always thought splitting named arguments from positional ones was ugly. Even if I still think
**rest
arguments are more clean than catch-all*rest
. But should’nt a debate like this happen on rcrchive?Daniel Berger
If the former, what’s special about the delegation abilities you mention? If the latter, how do you get around using an ordered hash?
Brian M.
I don’t like the simplified version. It seems to me that you are taking positional arguments and making them unpositional with some naming syntax sugar.
With Matz’s proposal we can treat named arguments like attributes of a call. I think the difference in potency is like night and day.
In the end I would rather not have named arguments if we don’t have something equivalent to Matz’s proposal. It just adds too much with little gain. I should make it more clear: Matz’s proposal and this one are not equivalent in fact they are quite different.
Maybe we can spend all of this energy trying to make syntax more intuitive than trying to accomplish something completely different.
Daniel Berger
I have absolutely no idea what you’re going on about Brian M. The only external differences between the Rite and Sydney implementations are in explicit declaration vs. implicit and the way *rest arguments are handled.
That’s true of any named argument scheme in existence.
Care to elaborate? I don’t follow.
So, somehow, Matz’s proposal, which requires more syntax, is not “too much”?
I’m not sure how you get more intuitive than foo(x:1, y:2), unless you want something like foo(:x=>1, :y=>2), which can also be done.
Matz
Daniel, take time to think about my slides please. Although it has more syntax, it’s much simpler in reality.
Foo#foo receives [{:a=>1, :b=>2, :c=>3}], since all keyword arguments are packed in a hash at the the last argument, thus it is contained in the rest argument (args this case). Baceuse Foo#bar takes three non-optional keyword arguments x, y and z, and arguments passed from foo (args) does not have either of them, so that Foo#bar causes ArgumentError.
Brian M.
I started writing a response but with the examples it became very long. I will be posting it to the ruby-talk ML shortly.
Clive
For symbols, couldn’t you just use this?
foo(1, b:(:two))
jan
Matz’s last example can be extended by adding: bar(args.sort.collect {|i| i + 1}. *args.sort may (also) become useful if you want to use unpositional constructs like: f.foo(“c:” << 1.to_s, ...; you may cut off “c:” again in the collect block). – For a different kind of hash, google for “ruby, pseudohash” (or use associative arrays: a = [[1,2,3], [4,5,6]]; p a[ 0].type in combination with hashes)!
Comments are closed for this entry.