A comparison by example of keyword argument styles

The separation between implementation and behavior you make here is lost
the moment you commit to supporting a keyword argument for any given
method, regardless of the fact that it was an explicit, conscious
choice. Once chosen, you're still beholden to that keyword name forever
unless you want to break backwards compatability. I think the perceived
freedom is an illusion.

There seems to be this fear of using an argument name, and then later
changing your mind about what the argument name should be called. To
that I reply that, even with the explicit syntax, this issue does not go
away. I would also say that all the implicit syntax does is force you
to think more up front about your API and naming conventions. With
Sydney, at least you can resort to positional parameters in the unlikely
event that the argument name did change, something you can't do with
Matz's current proposal afaik.

Regards,

Dan

···

-----Original Message-----

Amen. As I keep saying, it's a matter of interface. If you
allow implicit keyword arguments, you're suddenly mixing
interface and implementation, and that is *wrong* (in the
sense that it is difficult to maintain, and it breaks the
promise of encapsulation). However, if you explicitly decide
that some argument is a keyword argument, you have
*consciously* made it part of the interface (I'd rather say
part of the very *name* of the method), so you can still
maintain the separation between interface and implementation
to the level you want as a developer.

Hi --

Amen. As I keep saying, it's a matter of interface. If you
allow implicit keyword arguments, you're suddenly mixing
interface and implementation, and that is *wrong* (in the
sense that it is difficult to maintain, and it breaks the
promise of encapsulation). However, if you explicitly decide
that some argument is a keyword argument, you have
*consciously* made it part of the interface (I'd rather say
part of the very *name* of the method), so you can still
maintain the separation between interface and implementation
to the level you want as a developer.

The separation between implementation and behavior you make here is lost
the moment you commit to supporting a keyword argument for any given
method, regardless of the fact that it was an explicit, conscious
choice. Once chosen, you're still beholden to that keyword name forever
unless you want to break backwards compatability. I think the perceived
freedom is an illusion.

But that's the point: keyword arguments represent a decision to name
your arguments. It's just like a hash. If I say that my method takes
a hash:

   :first_name => ..., :last_name => ...

then of course I've committed to those keys, and can't change them
whenever I feel like it.

I just don't see why it has to be a winner-take-all situation, when
it's perfectly possible to have both keywords arguments and
"uncoupled" positional arguments.

There seems to be this fear of using an argument name, and then later
changing your mind about what the argument name should be called.

It's not a fear: it's a right :slight_smile: Having local variable names always
be part of the API just doesn't make sense to me at all.

To that I reply that, even with the explicit syntax, this issue does
not go away. I would also say that all the implicit syntax does is
force you to think more up front about your API and naming
conventions. With Sydney, at least you can resort to positional
parameters in the unlikely event that the argument name did change,
something you can't do with Matz's current proposal afaik.

But that means the caller has to keep track of whether or not the
maintainer of the code has change variable names. I don't want to be
on either end of that. It could be a refactoring and/or maintenance
nightmare.

David

···

On Fri, 21 Oct 2005, Berger, Daniel wrote:

-----Original Message-----

--
David A. Black
dblack@wobblini.net

Berger, Daniel wrote:

The separation between implementation and behavior you make here is lost
the moment you commit to supporting a keyword argument for any given
method, regardless of the fact that it was an explicit, conscious
choice. Once chosen, you're still beholden to that keyword name forever
unless you want to break backwards compatability. I think the perceived
freedom is an illusion.

There seems to be this fear of using an argument name, and then later
changing your mind about what the argument name should be called. To
that I reply that, even with the explicit syntax, this issue does not go
away. I would also say that all the implicit syntax does is force you
to think more up front about your API and naming conventions. With
Sydney, at least you can resort to positional parameters in the unlikely
event that the argument name did change, something you can't do with
Matz's current proposal afaik.

I must say, I was leaning with David, but these are good counter
arguments.

Perhaps we should just sing a song and "Let's Call The Whole Thing
Off". If keywords integrate the interface too tightly with the code,
then why tie them to the interface? Also, I imagine there many be many
more keyword arguments then ordered args, this will make for some very
LONG constructors:

  def stadium( height:, seating_capacity:, location:, color:, sports:,
concessions:, extra_features:, indoor:, yadayadayada: )

So maybe just treat named parameters like blocks. You can have em or
not. But you don't need to define them in the interface. Currently I do
a lot of

  def( *args )
    keys = args.last.is_a?(Hash) ? args.pop : {}
    a,b,c = *args

If I could just do

  def( a, b, c, **keys )
    ...

I'd be happy enough.

T.

P.S. But I don't much care for double character '**'. Single character
would be better. Perhaps '#' or '%'.

Selon "Berger, Daniel" <Daniel.Berger@qwest.com>:

The separation between implementation and behavior you make here is lost
the moment you commit to supporting a keyword argument for any given
method, regardless of the fact that it was an explicit, conscious
choice.

I happen to think that the fact that it was an explicit, conscious choice does
make a difference.

  Once chosen, you're still beholden to that keyword name forever

unless you want to break backwards compatability.

Yes, but you chose for it. In your implementation, the language chooses already
for the developer. Sorry, but although a language always makes some choices for
the developer, there are some that are more justified than others. This is one
in my opinion that is not justified.

  I think the perceived

freedom is an illusion.

That's your opinion. In my opinion it's very real.

There seems to be this fear of using an argument name, and then later
changing your mind about what the argument name should be called. To
that I reply that, even with the explicit syntax, this issue does not go
away.

No, but at least you chose for the situation.

  I would also say that all the implicit syntax does is force you

to think more up front about your API and naming conventions.

Too much in my opinion. Argument names shouldn't be part of the API unless one
explicitly chooses it that way. Also, having to think upfront about all the
details of your implementation, just to get a stable API, is not how software
is built. That's the main thing I don't like about your proposal: changing a
method's implementation under Sydney's implementation is much more likely to
change the API of the method than under Matz's proposal, *because you never
know who will call your arguments positionally and who will call them by
keyword.* Despite what you may think, the loss of freedom for the developer is
very real, and the coupling between interface and implementation far too strong
to be comfortable.

Sorry, but when I have positional arguments, I only want to have to worry about
their position for the API, and when I have keyword arguments, I only want to
have to worry about their names for the API. In your proposal I have to worry
about both, for all arguments. You seem to think that your proposal adds the
advantages of both styles. In my opinion it only adds together their drawbacks.
The advantages of keyword arguments disappear when there's a chance that someone
will call them positionally, and the advantages of positional arguments
disappear when there's a chance that someone will use them with keywords.

  With

Sydney, at least you can resort to positional parameters in the unlikely
event that the argument name did change, something you can't do with
Matz's current proposal afaik.

And then the programmer changes the order of the arguments because it fits
better the meaning of the definition and you lose anyway. Sorry, but I don't
think that's a gain. If the developer changes the API, change your program.
Such kind of stop-gap measure brings nothing but trouble. I think it only
encourages sloppiness, both for the developer and the user.

···

--
Christophe Grandsire.

http://rainbow.conlang.free.fr

It takes a straight mind to create a twisted conlang.

Berger, Daniel wrote:

> Amen. As I keep saying, it's a matter of interface. If you allow
> implicit keyword arguments, you're suddenly mixing interface and
> implementation, and that is *wrong* (in the sense that it is
> difficult to maintain, and it breaks the promise of encapsulation).
> However, if you explicitly decide that some argument is a keyword
> argument, you have *consciously* made it part of the interface (I'd
> rather say part of the very *name* of the method), so you can still
> maintain the separation between interface and implementation to the
> level you want as a developer.

The separation between implementation and behavior you make here is
lost the moment you commit to supporting a keyword argument for any
given method, regardless of the fact that it was an explicit,
conscious choice. Once chosen, you're still beholden to that keyword
name forever unless you want to break backwards compatability. I
think the perceived freedom is an illusion.

There seems to be this fear of using an argument name, and then later
changing your mind about what the argument name should be called. To
that I reply that, even with the explicit syntax, this issue does not
go away. I would also say that all the implicit syntax does is force
you to think more up front about your API and naming conventions.
With Sydney, at least you can resort to positional parameters in the
unlikely event that the argument name did change, something you can't
do with Matz's current proposal afaik.

I’m not pretending to understand what the whole discussion is about, but
as far as I understand, keyword arguments for Ruby as suggested by matz
are very much like those found in Common Lisp. I don’t see them having
a hard time using them. I think that the way keyword arguments are used
in Common Lisp makes a lot of sense. Give mandatory arguments as
positional arguments and then pass in any optional arguments as keyword
arguments in any order you see fit, e.g., the find function in Common
Lisp:

  find-if predicate sequence &key from-end start end key => element

OK, the two first arguments are always going to be a predicate that we want
to test with, and a sequence we want to traverse. Then we have
optionaly keyword arguments that narrow the search, if we are so
inclined. I don’t see the point of naming every argument that we pass
around.

        nikolai

···

--
Nikolai Weibull: now available free of charge at http://bitwi.se/\!
Born in Chicago, IL USA; currently residing in Gothenburg, Sweden.
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}

I'd quote stuff, but I'd be pulling from too many messages that it
would take forever :wink:

Anyway, here are my thoughts on the subject:

I really would like to see keyword arguments done right. Sydney's style
of all arguments are implicit keyword arguments I think is terrible.
The points above about being able to rename your args without worrying
about calling code is good, but also I believe that implicit keyword
arguments can make for inconsistent calling conventions.

Basically, keyword arguments should be explicitly defined. This is akin
to defining the name of the method - you're making a conscious API
decision. Implicit keywords makes every single argument an API decision
when it's simply not necessary.

In terms of syntax, I was perfectly happy with the

  m_f1(1, 2, a:3, b:4)

syntax until it was pointed out that this looks really terrible when
you pass symbols, as in

  m_f1(1, 2, a: :foo, b: :bar)

As was also mentioned above, I believe the best syntax is to borrow the
current implicit hash syntax, as in

  m_f1(1, 2, a=>:foo, b=>:bar)

This has several advantages. First off, it's a syntax that everybody is
familiar with. Secondly, if we add a backwards-compatibility feature of
treating

  m_f1(1, 2, :a => :foo, :b => :bar)

the same as the keyword version (i.e. with "a" and "b" isntead of ":a"
and ":b"), then it lends itself really well to making existing code use
keyword arguments. Any code that currently uses the implicit hash to
mock keywords can have the method redefined to use explicit keyword
arguments and the calling code will remain untouched, so the API will
remain the same. This I think is a huge plus. In terms of specifics for
this syntax, I think that methods without defined keyword arguments
should continue to use the implicit hash behaviour, and methods with
defined keyword arguments should no longer accept the implicit hash,
but rather if you want dynamic keyword arguments you should specify the
**args argument to grab all unknown keyword arguments. This way
existing code will work as-is, and can be slowly converted to use
keyword arguments wherever appropriate. This will also help with
methods that take mock keyword arguments because as soon as they're
changed to use explicit keyword arguments, then they automatically will
get argument checking on the keyword names, which can help people
trying to call that code (i.e. it will tell you if you typoed when
calling the method [unless, of course, the method takes arbitrary
keywords as well via **args]).

The only thing I'm not sure about right now is the proper syntax for
defining them. I don't mind the look of

  def m_f1(foo, bar, a:, b:)

except that it no longer makes sense once you're using => instead of :
in the calling conventions. Maybe we could do something like

  def m_f1(foo, bar, :a, :b)

or grab another symbol and use that, like

  def m_f1(foo, bar, %a, %b)

I personally prefer the look of the first (:a, :b).

Christophe Grandsire wrote:

<snip>

Yes, but you chose for it. In your implementation, the language chooses already
for the developer. Sorry, but although a language always makes some choices for
the developer, there are some that are more justified than others. This is one
in my opinion that is not justified.

But it *is* you who chooses it when you choose the parameter name. I don't feel the loss of control you seem to feel. I guess we're getting into programmer psychology here. :wink:

  I would also say that all the implicit syntax does is force you

to think more up front about your API and naming conventions.

Too much in my opinion. Argument names shouldn't be part of the API unless one
explicitly chooses it that way. Also, having to think upfront about all the
details of your implementation, just to get a stable API, is not how software
is built.

*All* of the details of the implementation? No, just the parameter names. That's a far cry from "all" IMO.

That's the main thing I don't like about your proposal: changing a
method's implementation under Sydney's implementation is much more likely to
change the API of the method than under Matz's proposal, *because you never
know who will call your arguments positionally and who will call them by
keyword.* Despite what you may think, the loss of freedom for the developer is
very real, and the coupling between interface and implementation far too strong
to be comfortable.

My reply to this would be that major refactorings usually break backwards compatability anyway, and the positional versus keyword argument becomes moot. That, or you have to jump through extra hoops to maintain it.

Sorry, but when I have positional arguments, I only want to have to worry about
their position for the API, and when I have keyword arguments, I only want to
have to worry about their names for the API. In your proposal I have to worry
about both, for all arguments. You seem to think that your proposal adds the
advantages of both styles. In my opinion it only adds together their drawbacks.
The advantages of keyword arguments disappear when there's a chance that someone
will call them positionally, and the advantages of positional arguments
disappear when there's a chance that someone will use them with keywords.

We'll have to agree to disagree. :slight_smile:

  With

Sydney, at least you can resort to positional parameters in the unlikely
event that the argument name did change, something you can't do with
Matz's current proposal afaik.

And then the programmer changes the order of the arguments because it fits
better the meaning of the definition and you lose anyway.

You lose with either implementation if positional parameters change (or even current Ruby).

Sorry, but I don't
think that's a gain. If the developer changes the API, change your program.
Such kind of stop-gap measure brings nothing but trouble. I think it only
encourages sloppiness, both for the developer and the user.

Encourages sloppiness? I think it will have the opposite effect.

I should also point out that Sydney *could* make keyword arguments optional by resorting to a more explicit behavior style API, e.g.

class Foo
    behavior KeywordBehavior
    def initialize(x, y, z)
       ...
    end
end

This would give all methods of class Foo implicit keyword parameters. But, perhaps we could make it even more granular (though I'm not certain without asking Evan). I'm just making stuff as I go here, but perhaps this:

class Foo
    behavior KeywordBehavior
    def bar(x, y, z)
       ...
    end
    def baz(a, b = 4)
       ...
    end

    # Add keyword support for parameter 'z' in method 'bar'
    KeywordBehavior.keyword_argument(:bar => "z")
end

Ok, it gets pretty verbose when you get that granular, but I thought I would toss it out there.

Regards,

Dan

Hi --

···

On Fri, 21 Oct 2005, Trans wrote:

So maybe just treat named parameters like blocks. You can have em or
not. But you don't need to define them in the interface. Currently I do
a lot of

def( *args )
   keys = args.last.is_a?(Hash) ? args.pop : {}
   a,b,c = *args

If I could just do

def( a, b, c, **keys )
   ...

I'd be happy enough.

T.

P.S. But I don't much care for double character '**'. Single character
would be better. Perhaps '#' or '%'.

   def meth(a,b,#c) that's a comment
   def meth(a,b,%w_w) # this one is ambiguous

David

--
David A. Black
dblack@wobblini.net

Hi,

···

In message "Re: A comparison by example of keyword argument styles" on Sat, 22 Oct 2005 07:25:26 +0900, Nikolai Weibull <mailing-lists.ruby-talk@rawuncut.elitemail.org> writes:

I’m not pretending to understand what the whole discussion is about, but
as far as I understand, keyword arguments for Ruby as suggested by matz
are very much like those found in Common Lisp.

Exactly. I was writing slides about keyword arguments with opening
the CLtL book aside.

              matz.

Sorry, but when I have positional arguments, I only want to have to worry about
their position for the API, and when I have keyword arguments, I only want to
have to worry about their names for the API. In your proposal I have to worry
about both, for all arguments. You seem to think that your proposal adds the
advantages of both styles. In my opinion it only adds together their drawbacks.
The advantages of keyword arguments disappear when there's a chance that someone
will call them positionally, and the advantages of positional arguments
disappear when there's a chance that someone will use them with keywords.

Well, I just realized reading the thread again that, syntax differences
aside, Sydney's proposal of making all parameters be allowed to be
named parameters is very akin to that of Python, while Matz proposal is
very much like Common Lisp. Sorry for being a tad slow... I
understand Matz dislike for python's approach now. I completely agree
from theoretical pov.

Question, however, is from a practical pov. Since python has supported
a Sydney-like function definition since v2.0 (ie. for the past 3 years
or longer), has this issue come up and has been a clear problem for
developers of libraries in that language?

David A. Black ha scritto:

The separation between implementation and behavior you make here is lost
the moment you commit to supporting a keyword argument for any given
method, regardless of the fact that it was an explicit, conscious
choice. Once chosen, you're still beholden to that keyword name forever
unless you want to break backwards compatability. I think the perceived
freedom is an illusion.

But that's the point: keyword arguments represent a decision to name
your arguments. It's just like a hash. If I say that my method takes
a hash:

  :first_name => ..., :last_name => ...

then of course I've committed to those keys, and can't change them
whenever I feel like it.

My 2c, some side view to add to this multifaceted debate:
I think you're supposing people will use your keyword arguments, which is in many cases unrealistic.
It does not matter what the names are if an user would use them only when they make sense (i.e. I won't call p(obj: foo) )

OTOH whenever you expect people to use your arguments by name (i.e. :rails =>stuff) you would commit anyway to keyword arguments even with the dual-scheme. Do you think otherwise?

I just don't see why it has to be a winner-take-all situation, when
it's perfectly possible to have both keywords arguments and
"uncoupled" positional arguments.

because it is much simpler, and it avoid the need for an author to choose beetween two different schemes.

There seems to be this fear of using an argument name, and then later
changing your mind about what the argument name should be called.

It's not a fear: it's a right :slight_smile: Having local variable names always
be part of the API just doesn't make sense to me at all.

I think they are already part of an api, since they are documentation.
They may not be strictly enforced but you're still not expected to change them since
  Time.new(duration)
is meaningful to people reading the api.

To that I reply that, even with the explicit syntax, this issue does
not go away. I would also say that all the implicit syntax does is
force you to think more up front about your API and naming
conventions. With Sydney, at least you can resort to positional
parameters in the unlikely event that the argument name did change,
something you can't do with Matz's current proposal afaik.

But that means the caller has to keep track of whether or not the
maintainer of the code has change variable names. I don't want to be
on either end of that. It could be a refactoring and/or maintenance
nightmare.

I also heard you can't have a reliable application written in dynamic languages, since they can be a refactoring and/or maintenance nightmare, the fact is that this is wrong :wink:

Python people have been using a merged-named-and-positional scheme for some years and it never shown itself as a problem (actually, I think it helps refactoring tools based on heuristics).

There could be other problems (i.e. the c api) with this scheme, but I think this kind of fear is unmotivated

David A. Black wrote:

> P.S. But I don't much care for double character '**'. Single character
> would be better. Perhaps '#' or '%'.

   def meth(a,b,#c) that's a comment

  puts "but this is #{not}"

   def meth(a,b,%w_w) # this one is ambiguous

did you mean?

   def meth(a,b,%w_w_) # this one is ambiguous

Yes, you're right.

In either case, I'd just prefer on one character rather than two
(reduce Perliness). But it's not a big deal.

Thanks,
T.

Yukihiro Matsumoto <matz@ruby-lang.org> writes:

Hi,

>I’m not pretending to understand what the whole discussion is about, but
>as far as I understand, keyword arguments for Ruby as suggested by matz
>are very much like those found in Common Lisp.

Exactly. I was writing slides about keyword arguments with opening
the CLtL book aside.

Certainly, more language designers should do this. :slight_smile:

···

In message "Re: A comparison by example of keyword argument styles" > on Sat, 22 Oct 2005 07:25:26 +0900, Nikolai Weibull <mailing-lists.ruby-talk@rawuncut.elitemail.org> writes:

              matz.

--
Christian Neukirchen <chneukirchen@gmail.com> http://chneukirchen.org

gga ha scritto:

Question, however, is from a practical pov. Since python has supported
a Sydney-like function definition since v2.0 (ie. for the past 3 years
or longer), has this issue come up and has been a clear problem for
developers of libraries in that language?

AFAIK, no, some pythonic friends seem to agree with this.
I still don't understand what the CLish proposal adds over a pythonic one (sydney's one + **kw. I do like **kw split from *args).

The only two things that I understand as problematic are
- c API
- some fuzzy sense of danger ("he's seeing my arguments' name!")

OTOH it is much more complex, IMHO

Hi,

···

In message "Re: A comparison by example of keyword argument styles" on Sun, 23 Oct 2005 11:32:00 +0900, "gga" <ggarra@advancedsl.com.ar> writes:

Question, however, is from a practical pov. Since python has supported
a Sydney-like function definition since v2.0 (ie. for the past 3 years
or longer), has this issue come up and has been a clear problem for
developers of libraries in that language?

I think Sydney-like function definition would not cause big problems.
But if I had to choose between Lisp and Python, I'd always choose the
former.

              matz.

List is great but I don't program in Lisp because of my eyes become
crossed in looking at it. I don't what the same thing in Ruby.

  def foo( c: ) # looks bad

  foo( c: :many ) # horrible!!!

I don't understand why this is acceptable. Symbols are common
arguments. Space significance is poo-poo'd everywhere else. If this is
allowed, why not

  class X
    def {}(x)
    end
  end

  X.new{1}

A _space_ can distinguish block:

  X.new {1}

To me it smacks of same thing. Worse, 'c::many' has whole other
meaning.

This is terrible. Why do you not care about this Matz? Why do so many
not seem to care about this?

T.

Hi,

List is great but I don't program in Lisp because of my eyes become
crossed in looking at it. I don't what the same thing in Ruby.

def foo( c: ) # looks bad

foo( c: :many ) # horrible!!!

It might be horrible, but it's at least different story from Lisp
ugliness.

This is terrible. Why do you not care about this Matz? Why do so many
not seem to care about this?

Just because it's better than other proposals. Even if there can be a
perfect solution, I haven't seen one yet.

              matz.

···

In message "Re: A comparison by example of keyword argument styles" on Sun, 23 Oct 2005 22:27:04 +0900, "Trans" <transfire@gmail.com> writes:

Trans wrote:

List is great but I don't program in Lisp because of my eyes become
crossed in looking at it. I don't what the same thing in Ruby.

  def foo( c: ) # looks bad

  foo( c: :many ) # horrible!!!

I don't understand why this is acceptable. Symbols are common
arguments. Space significance is poo-poo'd everywhere else.

I could live with this particular exception. Seriously, what are our
alternatives for keyword syntax in method calls?

"foo(x: 1)"
Pros: Probably easiest to parse. Familiar to Smalltalkers and Lispers.
Cons: Symbols force space, look ugly.
Aside: I don't expect to see a lot of folks passing symbols to keywords
arguments in practice.

"foo(x => 1)"
Pros: Familiar syntax, fairly intuitive.
Cons: More verbose, difficult (impossible?) to distinguish from a Hash.

"foo(x = 1)"
Pros: Clean, simple, intutive. Familiar to Pythonistas.
Cons: May very well be impossible to implement because '=' is an
expression.

"foo(x := 1)"
Pros: Avoids the SSW issue with symbols.
Cons: Unfamiliar syntax, smells like Ada.

If Evan can, by some wizardry, get '=' to work in Sydney, that is the
proposed syntax I would favor. Otherwise, I can certainly live with
":" and a forced whitespace for symbols.

This is terrible. Why do you not care about this Matz? Why do so many
not seem to care about this?

He does care. That's why he's joined this discussion in public, and
probably has some interest in seeing how Sydney does things.

Regards,

Dan

Trans wrote:

List is great but I don't program in Lisp because of my eyes become
crossed in looking at it. I don't what the same thing in Ruby.

  def foo( c: ) # looks bad

  foo( c: :many ) # horrible!!!

Since you write “foo( c: :many )” I don’t understand why you are worried
about not being able to remove the space between symbols and the keyword
argument. You obviously like your whitespace in abundance as you put in
the horribly looking spaces after and before opening and closing
parentheses.

        nikolai

···

--
Nikolai Weibull: now available free of charge at http://bitwi.se/\!
Born in Chicago, IL USA; currently residing in Gothenburg, Sweden.
main(){printf(&linux["\021%six\012\0"],(linux)["have"]+"fun"-97);}

He does care. That's why he's joined this discussion in public, and
probably has some interest in seeing how Sydney does things.

Point taken, though I didn't mean it like THAT, but that he didn't
address it when brought up in earlier posts.

I hate to say, but it seems like the selection of a character commonly
used for assignment operations, i.e. ':' as a prefix for symbols has
come back to haunt. Go back in time Matz! Throw out the '=>', use ':'
instead, and chose us another symbol symbol!

T.