Co-optional arguments?
Wed 1 Aug 2012 15:46 EDT
Racket and many other languages have the concept of optional arguments at the receiver side:
Here, the procedure is offering its caller the option of supplying a
value for foo
, and if the caller does not do so, uses 123
as the
value for foo. The caller is permitted not to care (or even to
know!) about the #:foo
option.
Almost no language that I know of has the symmetric case: an optional argument at the caller side (and because the feature doesn’t exist, I’m having to invent syntax to show what I mean):
The intent here is that the procedure is permitted not to care (or even to know) about the additional value its caller supplies.
This would be useful for callbacks and other situations where the code invoking a procedure has potentially-useful information that the specific (and statically-unknown) procedure being called may or may not care about.
I said above that almost no languages have this feature: it turns
out that Squeak and
Pharo have something similar in their
BlockClosure>>#cull:
methods. The following code works when given a
closure accepting zero, one or two arguments:
(Update: @msimoni points
out that
Common Lisp’s
allow-other-keys
is similar, too. It still requires the callee to declare something
extra, though.)
Comments (closed)
Add C and C++ to the list of languages with optional arguments. Yes, it's not Lispy, but it's been around a while... C/C++ can do this through varargs (which sucks), and you can also do it with function pointers and casting. The receiving code doesn't look at or care about additional parameters on the stack. C will even do it without casting, just compile with different header files.
But then, you'd have to program in C, which would be nasty.
Excellent point!
Ruby's slightly odd support for keyword arguments supports what co-optional arguments. In fact, it's kind of the default.
Ruby supports keyword arguments by collecting the keyword arguments into a Hash (= dictionary). For example (with ruby 1.9 syntax):
def foo opts={}
puts opts[:x].inspect
end
foo x: 10
# => 10
foo
# => nil
If you want to provide default values for these keyword arguments, you write something like:
def foo opts={}
opts = { x: 42 }.merge(opts)
puts opts[:x].inspect
end
foo x: 10
# => 10
foo
# => 42
But nothing stops the caller providing other keywords, which are ignored:
foo y: 20
# => 42
Despite the room for error, all Ruby code is written in that style. No-one goes to the trouble of detecting superfluous arguments, which would require writing something like:
def foo opts={}
opts = { x: 42 }.merge(opts)
opts.keys.all? { |k| [:x, :y].include? k } or raise "unexpected keys"
puts opts[:x].inspect
end
That is very close, too. It does still require the receiver to expect a hash, though. A truly 0-ary function raises ArgumentError when given such a keyword argument.
Jason's point about C, and your point about Ruby, make me realise that Javascript also supports something along these lines.
JavaScript is similar. Extra arguments are discarded, extra parameters get 'undefined'.