In an earlier article, Default and Named Parameters in C# 4.0 / Sith Lord in Training, I wrote about how C# 4.0 – that’s the version coming out with the next release of Visual Studio, known as Visual Studio 2010 – is going to provide support for named parameters.
In that article, I also incorrectly stated that Ruby supported named parameters. Luckily, Jörg W Mittag spotted my mistake an corrected me in a comment. I’ve since corrected the article and thought I’d show you how I got it wrong in the first place.
Ruby and My Named Parameter Goof
I had a vague recollection of Ruby accepting named parameters. I figured I’d be empirical and fired up irb – the Ruby REPL shell – and put together a quick little method to see if the recollection was correct:
# Ruby 1.8.6 def test_named_params(first, second) puts "#{first}\n#{second}" end
Once put together, I made some test calls to the method:
# irb session (Ruby 1.8.6) irb(main):> test_named_params("alpha", "beta") alpha beta
=> nil irb(main):> test_named_params(first = "alpha", second = "beta") alpha beta
=> nil
Seeing that the interpreter didn’t choke on that named parameter call, I thought to myself “Vague recollection confirmed, Ruby supports named parameters!” and wrote the blog article.
Had my brain actually been firing on all cylinders, I would’ve given the method a proper test by providing the named parameters out of the order in which they appear in the method signature. Here’s what I would’ve seen:
# irb session (Ruby 1.8.6) irb(main):> test_named_params(second = "alpha", first = "beta") alpha beta=> nil
Uh-oh. If named parameters worked, the first output line would be “beta” and the second would be “alpha”. Clearly something’s wrong with my recollection.
Let’s try some non-existent named parameters – say, ones involving current entertainemtn news headlines — just to see what happens:
# irb session (Ruby 1.8.6) irb(main):> test_named_params(lindsay_lohan_dui = "alpha", jim_cramer_smackdown = "beta")alpha
beta
=> nil
Even with nonsensical named parameters, the method is still accepting the values in order. Why is that?
Just about everything in Ruby has a return value (which can be anything, including nil
). You can see for yourself in irb – here’s a quick do-nothing method definition:
irb(main)> def doNothing irb(main)> end => nil
As you can see. defining a method returns a value of nil
.
As Jorg pointed out, Ruby assignment statements return a value: the value used in the assigment. Once again, for proof, I’ll use an example from an irb session. In the example below, assigning the string "alpha"
to the variable first
also returns the string "alpha"
:
# irb session (Ruby 1.8.6) irb(main):> first = "alpha"
=> "alpha"
In the call to test_named_params
, the Ruby interpreter was interpreting my “named parameters” as assignment statements. first = "alpha"
evaluates to plain old "alpha"
, but so does second = "alpha"
(and for that matter, so does lindsay_lohan_dui = "alpha"
). Each assignment statement in my parameter list was evaluated, and then those values were passed to method in positional order.
Python Supports Named Parameters
After getting the comment from Jorg and correcting my article, I wondered why I thought Ruby supported named parameters. Then it hit me – it’s Python.
So I fired up the Python REPL and put together this quick little method:
# Python 3.0 def test_named_params(first, second): print("%s\n%s" % (first, second))
And this time, I decided to be a little more thorough in my testing:
# Python 3.0 REPL >>> test_named_params("alpha", "beta") alpha beta >>> test_named_params(first = "alpha", second = "beta") alpha beta >>> test_named_params(second = "alpha", first = "beta") beta alpha
And some additional searching on the web confirmed that yes, Python method calling does in fact support named parameters.
So in conclusion, when it comes to named parameters, it’s Python si, Ruby no…and C# pronto.
13 replies on “Named Parameters in Method Calls: Python Si, Ruby No”
Who actually uses named parameters? If you have enough arguments that you can’t remember what goes where then you’re doing it wrong. Named parameters are a mitigation for poor design. I’d be surprised to see any use of named parameters in Django’s source.
I’m a big fan of named parameters, because it simply makes the intention of the calling code clearer – it’s easier for the _maintainer_, because you don’t have to go around looking up function signatures.
You may also want to add in here Objective-C – iPhone programming – and Smalltalk, as if I remember correctly more or less all parameters are named. Javascript by convention uses passing in a dictionary to achieve a similar result. URL parameters to APIs are effectively RESTful named parameters.
You can use a dictionary to pass in named parameters in Python:
d = { “second” : “alpha”, “first” : “beta” }
test_named_params(**d)
Also see two blog posts, the anything goes pattern for specialized catchalls, and my bitching at MS for not using named parameters!
How would you make a _practical_ programming API to this without named parameters. Hmmm. How does Ruby do it? (really)
Django does in fact use named parameters – I just happen to have a copy of the source code with me here on vacation however did that happen …
$ find . -name “*.py” | xargs grep Paginator
./contrib/admin/views/main.py:from django.core.paginator import Paginator, InvalidPage
./contrib/admin/views/main.py: paginator = Paginator(self.query_set, self.list_per_page)
./contrib/comments/views/moderation.py:from django.core.paginator import Paginator, InvalidPage
./contrib/comments/views/moderation.py: paginator = Paginator(qs, 100)
./contrib/sitemaps/__init__.py: self._paginator = paginator.Paginator(self.items(), self.limit)
./core/paginator.py:class Paginator(object):
./views/generic/list_detail.py:from django.core.paginator import Paginator, InvalidPage
./views/generic/list_detail.py: paginator = Paginator(queryset, paginate_by, allow_empty_first_page=allow_empty)
Some thoughts on named parameters:
someMethod(5, "fudge", , , , , True, , , "x")
when it would’ve been nicer to just havesomeMethod(iterations = 5, flavor = "fudge", dontWhizOnElectricFence = True, discountCode = "x")
myMethod(flavor = "fudge", sprinkles = False)
andmyMethod :flavor => "fudge", :sprinkles = false
.There is an important difference between multipart message selectors in Smalltalk (and of course also its derivatives Self, Newspeak and Objective-C) and keyword arguments: as Joey demonstrated, order of keyword arguments doesn’t matter – that’s more or less the point of keyword arguments. In Smalltalk, however, there are no keyword arguments. Instead, the method name is broken into pieces, and the arguments are written in between the pieces. So, in Python, both
test_named_params(first = "alpha", second = "beta")
andtest_named_params(second = "beta", first = "alpha")
send the same message (test_named_params
) and thus execute the same method. In Smalltalk,self testNamedParamsFirst: "alpha" second: "beta".
andself testNamedParamsSecond: "beta" first: "alpha".
send two different messages: the first one sendstestNamedParamsFirst:second:
and the second one sendstestNamedParamsSecond:first:
, thus invoking two different methods!So, to regurgitate the infamous geometric shapes example: in Python, you would have a class
Rectangle
, with a constructor that takes two keyword parameters,width
andheight
and it wouldn’t matter in which order you pass the arguments. In Smalltalk, instead of the constructor, you would have a factory method calledwidth:height:
and trying to pass arguments the other way round would result in a No Method Exception, unless you also define aheight:width:
method.Jorg W Mittag: Excellent point. And as he said, it’s the same in Objective-C.
By no means am I a Ruby guy, but in the non-sensical named parameters example there were some extra line breaks. I suspect it’s just formatting throw up. Or is that the actual output? Looks hinky if it’s the actual output.
Arggg … lengthy comment destroyed by hotel internet.
Very useful info – esp since I may do some Objective C programming soon. Useful link. Anyone want to chime in about other languages. Lisp? Mathematica?
Named Parameters are used Heavily in Smalltalk, Sometimes you have a very complicated interface that is best dealt with in one shot instead of dragging it out.
In OCaml if you have named parameters you can keep currying the function til you’re done filling out the parameters.
In perl named parameters are just
sub { my %params = @_; … } and everything is in the params hash (really common place in perl)
Surely you could just pass a hash in ruby
Another great use for named arguments is that you can use boolean parameters that are otherwise opaque and without creating ad hoc two-member enumerations—doSomething(ansychronous: false) vs. doSomething(false) vs. doSomething(DoSomething.Synchronous).
@Carsten: Is 3 too many arguments to remember where they all go?
Hint: http://www.google.com/codesearch?hl=en&lr=&q=memset\s*\(.%2B%2C.%2B%2C0&sbtn=Search ;)
@David: Drop by PyGTA tonight, and I’ll show you some of the Objective-C I’ve been writing…
Later,
Blake.
Ruby doesn’t do named parameters in this way, but it does support the idiom:
# Ruby 1.8.6
def test_named_params(params)
puts "#{params[:first]}\n#{params[:second]}"
end
# irb session (Ruby 1.8.6)
irb(main):004:0> test_named_params(:first => 'alpha', :second => 'beta')
alpha
beta
=> nil
irb(main):005:0> test_named_params(:second => 'alpha', :first => 'beta')
beta
alpha
=> nil
irb(main):006:0> test_named_params(:first => 'beta', :second => 'alpha')
beta
alpha
=> nil
Note that Ruby converts the method parameters into a single hash. This approach is used a lot in Rails, for example to simplify the process of associating form parameters with model attributes.
Alas Blake, I was on an airplane. I’m thinking of going to that Thursday night Python think that that Leah person was talking about at DemoCamp.