Hi Simon,
Sometimes I find myself writing :key=3D>true,
it would be nice if one didn't had to do assignment,
so its value instead defaulted to nil.
=20
def test(hash=3D{})
p hash
end
=20
test('x', 'y'=3D>2, 'z') # {"x"=3D>nil, "y"=3D>2, "z"=3D>nil}
Matz> In that case, how can we distinguish a hash key
Matz> without value and ordinal mandatory argument?
Theoretically, you could allow flag arguments *after* the
first hash entry,
def test(hash=3D{}) ... end
test :x =3D> 2, :y, :z
but then of course you wouldn't be able to pass flag
arguments *only*.
Maybe I don't understand the mandatory argument thing?
But can't you just count them somehow?
def test(arg1, arg2, hash=3D{})
puts "#{arg1.inspect} #{arg2.inspect} #{hash.inspect}"
end
# mandatory argument?
test(1, 2) # 1 2 {}
# hash key without value
test(1, 2, 42) # 1 2 {42=3D>nil}
test(1, 2, 3, 4) # 1 2 {3=3D>nil, 4=3D>nil}
test(1, 2, 3, 4=3D>5) # 1 2 {3=3D>nil, 4=3D>5}
You can always do this:
class Array
def butlast(n=3D1) slice 0 ... -n end
def to_option_hash
if last.kind_of? Hash
then hash =3D last ; flags =3D butlast
else hash =3D {} ; flags =3D self end
for flag in flags do
flag.to_sym or raise ArgumentError,
"cannot convert flag option `#{flag}' to symbol"
hash[flag.to_sym] =3D true
end
hash
end
end
def test(arg1, arg2, *args)
options =3D args.to_option_hash
puts "#{arg1.inspect} #{arg2.inspect} #{options.inspect}"
end
That reminds me, I promised I would write an RCR for
allowing trailing argument after the splatting one, like so:
def moomin(foo, *bar, baz)
It was said that optional arguments should be allowed before
the splat, but not after it.
def moomin(foo, optional=3Dnil, *bar, baz)
In the above case of option hashes with flags, however, it
is obvious that it would be useful to be able to have
optional arguments after the splat.
def test2(arg1, arg2, *flags, options=3D{})
The above is unambiguous: we must always try to assign one
argument to =E2=80=98options=E2=80=99 =E2=80=94 anything else wouldn't ma=
ke sense.
It only becomes a problem when you have optional arguments
both before *and* after the splat:
def test3(arg1, arg2, arg3=3Dnil, *flags, o=3D{})
Obviously, when =E2=80=98test3=E2=80=99 is called with four arguments, we
must assign the third to =E2=80=98arg3=E2=80=99 and the fourth to =E2=80=98=
options=E2=80=99,
but what if only three arguments are given =E2=80=94 does the last
one go to =E2=80=98arg3=E2=80=99 or to =E2=80=98options=E2=80=99?
I think the only reasonable thing to do is to fill optional
argument slots from left to right, as is done now:
def foo(a, b=3Dnil, *c, d=3Dnil, e) ... end
foo 1 # ArgumentError
foo 1, 2 # a=3D1, b=3Dnil, c=3D[], d=3Dnil, e=3D2
foo 1, 2, 3 # a=3D1, b=3D2, c=3D[], d=3Dnil, e=3D3
foo 1, 2, 3, 4 # a=3D1, b=3D2, c=3D[], d=3D3, e=3D4
foo 1, 2, 3, 4, 5 # a=3D1, b=3D2, c=3D[3], d=3D4, e=3D5
This would also reasonably mean that mixing optional and
mandatory arguments would become allowed:
def bar(a, b=3Dnil, c, d=3Dnil, e) ... end
foo 1 # ArgumentError
foo 1, 2 # ArgumentError
foo 1, 2, 3 # a=3D1, b=3Dnil, c=3D2, d=3Dnil, e=3D3
foo 1, 2, 3, 4 # a=3D1, b=3D2, c=3D3, d=3Dnil, e=3D4
foo 1, 2, 3, 4, 5 # a=3D1, b=3D2, c=3D3, d=3D4, e=3D5
What do people think? Does this all seem reasonable?
Eric Mahurin had another good example of a use case:
def []=3D *coordinates, value
--=20
Daniel Brockman <
[email protected]>
So really, we all have to ask ourselves:
Am I waiting for RMS to do this? --TTN.
PS: Note how I've started putting parens in the argument
lists of method definitions and leaving out the spaces
around the equals signs for optional arguments. Somewhere,
Guido is chuckling with satisfaction. I hope you are too.