A comparison by example of keyword argument styles

C

Christophe Grandsire

En r=E9ponse =E0 itsme213 :
=20
Their number is part of the signature (rather than their position(s)).
=20

OK. That's about what I meant :) .
=20
I have always preferred this semantics.
=20
It also means keyword argument names must be available through reflecti= on.
=20

Indeed, very good remark.
--=20
Christophe Grandsire.

http://rainbow.conlang.free.fr

You need a straight mind to invent a twisted conlang.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: A comparison by example of keyword argument styles"

|> def foo(*args)
|> # how do you delegate arguments?
|> bar(*args)
|> end
|>
|> def bar(a,b,c)
|> p [a,b,c]
|> end
|>
|> foo(1,a:2,c:3)
|
|Not allowed because redefinition of the same parameter raises an error. Here
|you're trying to define 'a' twice, once as the first positional argument, once
|as a named parameter.

Then what if foo(1,b:2,c:3) ?

|Yes, it's uglier. How does it work in your implementation? I'm curious to know
|how you preserve argument order.

For mine, args in foo becomes [1,{:a=>2,:c=>3}]. And suppose

def bar(a:,b:,c:)
p [a,b,c]
end

since it requires explicit keyword arguments, delegation should be
plain

bar(*args)

and bar prints [1,2,3].

|Let me ask you the same thing. What happens here with your implementation?
|
|class Foo
| def foo(a:, b:, c:)
| p [a,b,c]
| end
|end
|
|class Bar < Foo
| def foo(d:, e:, f:)
| super
| end
|end
|
|Bar.new.foo(f:3, e:2, d:1)

Bar#foo receives [{:f=>3,:e=>2,:d=>1}], then it delegates arguments to
Foo#foo. Foo#foo accepts keyword arguments a, b, c, and no other
keywords (it does not have ** in the list), it raises ArgumentError.

matz.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: A comparison by example of keyword argument styles"

|Matz, are there any patches that implement your proposal yet?

Not yet. The behavior was fixed on a night before the keynote. It is
much easier to implement than one in Sydney though.

matz.
 
E

ES

Yukihiro said:
Hi,

In message "Re: A comparison by example of keyword argument styles"

|Ruby has always favored implicitness over explicitness. The fact that
|I don't have to do anything explicitly in Sydney makes it the winner
|IMHO.

Too much implicitness makes code cryptic. Too much explicitness makes
code verbose. It's a matter of balance.

By the way, how do you delegate the whole arguments (including keyword
arguments) in your style? Using KeywordBehavior?

Besides, what would happen for the following code?

def foo(*args)
# how do you delegate arguments?
bar(*args)
end

def bar(a,b,c)
p [a,b,c]
end

foo(1,a:2,c:3)

and

class Foo
def foo(a,b,c)
p [a,b,c]
end
end
class Bar<Foo
# what if argument names differ?
def foo(d,e,f)
super
end
end
Bar.new.foo(d:1,e:2,f:3)

These may not be the best arguments. Two scenarios are possible:

1) foo is supposed to be a transparent proxy: the caller must take care
to call it correctly, like the proxied object/method expects.

2) foo is supposed to present a real interface: the programmer must
take care that the arguments it receives are properly mapped to bar.

In both cases, an error should be raised if keyword arguments are
passed in with nonexistent keywords.

E
 
G

gga

Yukihiro Matsumoto ha escrito:
Hi,

In message "Re: A comparison by example of keyword argument styles"

|Matz, are there any patches that implement your proposal yet?

Not yet. The behavior was fixed on a night before the keynote. It is
much easier to implement than one in Sydney though.

matz.

One question. Why this complexity in syntax? Why the strange and
very confusing ':' as the syntax delimiter in both function definition
and, worse, parameter passing? Sorry, but this just feels so
complicated to me and seems more something thought with ruby's parser
in mind, not the user.

What's wrong with '=' and avoiding defining explicit parameters in the
parameter definition.

Compare everything that was proposed with python's simplicity and
elegance:

# this is something akin to ruby's: def f(*args)
def f2(a, b, c, *d):
print a, b, c, d

# and this is akin to ruby's: def f(arg={})
def f(a, b, c='xxx', **d):
print a, b, c, d

# ---------------

f(1,2,3) # => 1 2 3 {}
f(a=4, b=5, c=8) # => 4 5 8 {}
f(3, b=5, c=8) # => 3 5 8 {}
f(b=5, 3) # SyntaxError: non-keyword arg after keyword arg
f(1,2,3, x="hello") # => 1 2 3 {'x' : "hello"}
f(1,2) # => 1 2 xxx {}

f2(1,2, 3, "hello", "a", "c") # => 1 2 3 ( 'hello', 'a', 'c')


There's a lot of things I don't like in python, but how functions are
defined is definitively one of the strongest things in the language.
As you can see above, things are much simpler. No need to use any
special syntax for named parameters in the function definition and the
use of '=' is a heck of a lot more clear than : or =>. Both array and
hash syntaxes for extra parameters are also accounted for.
 
T

Trans

What's wrong with '=' and avoiding defining explicit parameters in the
parameter definition.

But = can be used to do assignment at the moment of passing:

def foo(x)
x
end

a = nil

foo (a=1) #=> 1

a #=> 1

T.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: A comparison by example of keyword argument styles"

|What's wrong with '=' and avoiding defining explicit parameters in the
|parameter definition.

Unlike Ruby, assignments are expression in Ruby. Besides that, I
don't think Python keyword arguments are simple and elegant, but YMMV.

matz.
 
D

Daniel Schierbeck

Yukihiro said:
Hi,

In message "Re: A comparison by example of keyword argument styles"

|1) *Method definition*
| I think named arguments should have the same
| syntax as positional ones, just with a colon
| in front of it.
|
| def foo(a, b, :c, :d = "bar")

Why with a colon in front of it instead of right after it?

|2) *Method calling*
| I think matz' style is preferable in many cases,
| but i still think the :key = value syntax should
| be there.

Why should be?

matz.
Because the keys *are* symbols, and they have colons in front of them.


Cheers,
Daniel
 
D

Daniel Schierbeck

Damphyr said:
The :a syntax looks a lot like symbols ...

Erm, the keywords *are* symbols.

def foo(a: 1, b: 2, **keys)
puts keys[:a] # :a is a symbol!
end

Therefore i think it's more clear if we write the keys as symbols.

def foo:)a = 1, :b = 2, **keys)
puts keys[:a] # foo:)a) -> keys[:a]
end


Cheers,
Daniel
 
T

Trans

Because the keys *are* symbols, and they have colons in front of them.

But they're *not* symbols. They're local vars.

T.
 
D

Daniel Schierbeck

Trans said:
But they're *not* symbols. They're local vars.

T.

def foo(a:, **keys)
puts keys[:a] # the key "a" is a symbol
end

I'd agree if the named arguments worked like positional ones, ie

def foo(a:)
puts a
end

But unless I've completely misunderstood what's been suggested here,
that's not the case.


Cheers,
Daniel
 
T

ts

D> def foo(a:)
D> puts a
D> end

Well, matz has given the reason

vgs% cmucl -quiet
* (defun foo (&key a) (list a))

FOO
* (foo :a 12)

(12)
* (quit)
vgs%

OK ?


Guy Decoux
 
T

ts

D> def foo(a:, **keys)
D> puts keys[:a] # the key "a" is a symbol
D> end

and to give you another example

vgs% cmucl -quiet
* (defun foo (&rest keys &key a &allow-other-keys)(list keys a))

FOO
* (foo :a 12)

(:)A 12) 12)
* (foo :a 12 :b 24)

(:)A 12 :B 24) 12)
* (quit)
vgs%


Guy Decoux
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: A comparison by example of keyword argument styles"

|Unlike Ruby,

I meant "unlike Python".

matz.
 
D

David A. Black

Hi --

Damphyr said:
The :a syntax looks a lot like symbols ...

Erm, the keywords *are* symbols.

def foo(a: 1, b: 2, **keys)
puts keys[:a] # :a is a symbol!
end

Therefore i think it's more clear if we write the keys as symbols.

def foo:)a = 1, :b = 2, **keys)
puts keys[:a] # foo:)a) -> keys[:a]
end

:a = 1 parses visually like assignment to a symbol, and therefore
looks bizarre.


David
 
G

gga

Trans said:
foo (a=1) #=> 1

Yes, I am aware of that. But can you actually point to me some library
code that actually uses this as a feature? I have most definitively
never seen any ruby code using that as of yet, and even then, I would
say that variable assignment within an expression is just something to
win the obfuscated code award, not a useful functionality.

Matz mentioned:
Besides that, I don't think Python keyword arguments are simple and elegant, but YMMV.

Could you elaborate why you prefer the other syntax? I can give you my
reasons for strongly suggesting following python's syntax, instead of
what you proposed.

To me, python's approach is simpler because:
a) There is no need for re-definiing a lot of previously written code
with a new syntax. If you have already written a function like
def x(a,b,c)
with the python automatic naming syntax, it makes the code still
work both positionally (as before) and now as named parameters --
automatically. Using a new syntax like a: or whatever, forces you to
basically have to rewrite all previously written code to make it work
with named parameters. While certainly functions that have used hashes
to accomplish a similar approach will need to get updated (or those
that used non-sensical names for function parameters), there shouldn't
be the need to update all previously written functions that did not
suffer from this. By not using any new special symbols, you gain the
functionality in pretty much most already written functions -- all of
ruby library, for example. A big, big plus, in my opinion. For the
parser and interpreter, it does makes things harder, but that's more or
less as it should be -- complexity on the side of the interpreter, not
on the side of the user.

b) There is a symetry between default parameter values and passing new
values to parameters in functions. This makes the code easier to
comprehend and the language easier to learn. That is:
def function(a, b = 2, c = 3):
...
function( 1, b = 4, c = 5)

or (sans parenthesis)
function b = 4, c = 5

(Yes, I know that with the current use of = in ruby, the above
"function b = 4, c = 5" makes it impossible for the interpreter to
disambiguate, but assume dispatching of equality was not allowed within
functions -- as it isn't during a def definition, so that parsing the
above should be possible).

Compared to (if I understood the proposal correctly):
def function(a, b: = 2, c: = 3) # ugly
end

function( 1, b : 4, c : 5)
function b: 4, c: 5 # use of : looks nicer, but
see also point c for issues

In this proposed ruby syntax, colons are used as assignment in
one situation, while in another situation, it is equality that it is
used. Confusing.

c) ruby already uses the :x syntax to denote symbols and, in the
ruby1.9 head also for hash definitions with symbol keys. This presents
another big, big, big source of confusion.
There was already a mail in this thread that already showed the big
confusion this leads to, as someone used the symbol notation in the
function definition incorrectly. Also, compare the invokation issues:
function to = :london, from = :paris
to:
function to: :london, from: :paris

The first example can not lead to typos that the interpreter will
misinterpret.
However, the second example can lead to two very easy to make
typos:
function :to :london # oops, two symbols used, etc. probably
flagged as syntax error during parsing?
function to::london # oops, this can NOT flagged as syntax
error, but leads to a runtime error only. Bad.

True, with the appearance of named parameters, the use of symbols
in function invokations will probably be much less than what it is now,
but...

Anyway, my 2 cents on the issue.
 
G

gga

Could you elaborate why you prefer the other syntax? I can give you my
reasons for strongly suggesting following python's syntax, instead of
what you proposed.

Never mind me. I read David Black's response and I see the point.

Still, if the positional / named argument definition is chosen, could I
also suggest that a symbol (or keyword) is used to delimit positional
vs. named arguments, without having to specify it on each variable?

That is, instead of:

def function( a, b, c, d:, e: = 1, f: )
end

Something along the lines of:

def function( a, b, c : d, e = 1, f )
end

where parameters to the left of the colon (or whatever symbol/keyword
you choose) are positional parameters and those to the right are named
ones. A more readable symbol for this would be "!" or "|" but
obviously these conflict with ruby's use of those for negation/or.
This would somewhat help solve the issue of the definition being so
easily confused with symbols, make the function more readable (imo) and
would lead to less typing, too. If your model is CLISP, this is also
how CLISP deals with this (using &key), like:

(defun foo ( &key a b c ) (list a b c))

Thanks... sorry for my 3 emails.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: A comparison by example of keyword argument styles"

|That is, instead of:
|
| def function( a, b, c, d:, e: = 1, f: )
| end

Right syntax is

def function(a, b, c, d:, e: 1, f:)
end

|Something along the lines of:
|
| def function( a, b, c : d, e = 1, f )
| end
|
|where parameters to the left of the colon (or whatever symbol/keyword
|you choose) are positional parameters and those to the right are named
|ones.

|If your model is CLISP, this is also
|how CLISP deals with this (using &key), like:
|
| (defun foo ( &key a b c ) (list a b c))

I considered that idea too, but I chose similarity to calling
syntax.

matz.
 
N

nobu.nokada

Hi,

At Sun, 23 Oct 2005 21:32:30 +0900,
Yukihiro Matsumoto wrote in [ruby-talk:162119]:
|Something along the lines of:
|
| def function( a, b, c : d, e = 1, f )
| end
|
|where parameters to the left of the colon (or whatever symbol/keyword
|you choose) are positional parameters and those to the right are named
|ones.

|If your model is CLISP, this is also
|how CLISP deals with this (using &key), like:
|
| (defun foo ( &key a b c ) (list a b c))

I considered that idea too, but I chose similarity to calling
syntax.

What about semicolon like as block parameters.

def function(a, b, c = ""; d, e = 1, *f)
end
 
E

Eric Mahurin

--- [email protected] said:
Hi,
=20
At Sun, 23 Oct 2005 21:32:30 +0900,
Yukihiro Matsumoto wrote in [ruby-talk:162119]:
|Something along the lines of:
|
| def function( a, b, c : d, e =3D 1, f )
| end
|
|where parameters to the left of the colon (or whatever symbol/keyword
|you choose) are positional parameters and those to the right are named
|ones.
=20
|If your model is CLISP, this is also
|how CLISP deals with this (using &key), like:
|
| (defun foo ( &key a b c ) (list a b c))
=20
I considered that idea too, but I chose similarity to calling
syntax.
=20
What about semicolon like as block parameters.
=20
def function(a, b, c =3D ""; d, e =3D 1, *f)
end

At first glance I like this, but ...

- with only keyword args, it looks a little ugly:
def function(; d, e =3D 1, *f)

- in 1.9, ";" is already used as a delimiter (in the argument
list) for block local variables. I assume keyword args will be
allowed with blocks/lambdas.

Just for reference, another language that takes "keyword"
arguments is verilog (hardware language). It is ugly, but it
looks like this:

foo ( .a(1), .b(2), .c(3) ) # order doesn't matter
foo ( 1, 2, 3 ) # a=3D1, b=3D2, c=3D3




=09
=09
__________________________________=20
Yahoo! Mail - PC Magazine Editors' Choice 2005=20
http://mail.yahoo.com
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top