Right now an argument's name is both external and internal:
def foo(arg)
# here (internal to the method) we use `arg` to access the argument
puts arg
end
# here (external to the method) we use `arg` to pass the argument
# (of course we can pass it positionally, but that's a different story)
foo arg: 2
We could optionally allow specifying an external name that's different to the internal one. This would be like in Swift:
def foo(external internal)
puts internal
end
foo external: 2
This makes the method signature, and the method's body easier to read:
class Person
def meet(with person)
puts "I met with #{person}"
end
end
john = Person.new
doe = Person.new
john.meet with: doe
This also enables something that we can't do right now, and that's using keywords such as class
and module
as argument names:
# I think this line parses well
def foo(class)
# but here we can't use class as a variable... (syntax error)
end
# but we can still do this:
foo class: Int32
With this change:
def foo(class a_class)
puts a_class
end
foo class: Int32
Of course, defining an external name is totally optional.
What do you think?
I think it's way better to simply re-assign the arg to a local var - LLVM will optimize away the additional step. Complicating signature syntax doesn't make code clearer.
class Person
def meet(with, at)
person = with; time = at
puts "I met with #{person} at #{time}"
end
end
I like the idea of having a longer, self-documenting external name and a terse internal name.
While @ozra makes a fair point, synchronizing methods with several params gets to be tedious. I'd prefer to have the option to have them paired in the parameter list.
I think in the general case the "external" name would be the more readable one, there's no reason to obscure the method by using a more cryptic "short" variant, after all code is far more often read than written. Let's encourage readable parameter names for both, the call side and the definition. Also different names for the same thing is just increasing the number of synonyms you need to keep in mind. Finally this is just one more concept a language user would need to learn, for (IMO) little to no benefit.
The only advantage I see is been able to use language keywords as named arguments. if:
, class:
, and when:
are those kind of words every now or then you want use.
But maybe something simpler could be done to allow this scenario: _if the argument name begins with an _ underscore allow the also the non underscored name to be used as external name_ or something along that idea.
def foo(_class)
puts _class
end
foo class: Int32 # => Int32
My opinion is the same as @jhass: this adds very little to the language and it's a concept everyone will eventually have to learn. Keywords as external arguments are very rare and in any case you can use klass
, an_if
, etc.
I created the isssue because @waj likes externa/internal names from Swift, and I wanted to know others' opinions. Seems there are good arguments on both sides :-)
The leading underscore idea feels too "special case" to me.
Would be interesting to know what Swift developers think of this feature, and how often it's used.
Certainly non-essential, but I do like it.
@Perelandric As far as I know Swift needs to be compatible with Objective-C, and Objective-C has external/internal names and the external names are part of the signature (ABI). So maybe they needed external names in order to be compatible, but I don't know. Maybe someone else can confirm this.
@asterite
Sounds like it would be a plausible reason for its inclusion in Swift, though I meant it's "non-essential" as a Crystal feature from my perspective. I like it but can easily live without it.
Objective-C has keyword method names like Smalltalk, so the external names are probably used to build the veryLong:andDescriptive:methodName:
and used as named arguments if present.
This solves the Ruby issue with kwargs. Often the calls read well, but then you're stuck with variable names that mean nothing. For example Comment.moderated(by: user)
is great, but by
is terrible internal naming.
Reassigning is a solution but not a pretty one. I think like the proposal, kinda alien, but it reads damn well. To be fair I must admit my first reaction was: WTF.
I remembered that they also have another use: forcing an argument to be passed as a positional argument. For example:
# I really don't care about the argument names, but I have to choose them
def add(x, y)
x + y
end
# Then somebody uses it like this:
add x: 1, y: 2
# Later I decide to change the names of the arguments
# Oops, I broke someone's code
def add(left, right)
left, right
end
# With external names I can do this:
def add(_ x, _ y)
x + y
end
add 1, 2 # OK
add x: 1, y: 2 # Error
That is, an underscore as an external name forces an argument to be passed positionally.
It's a minor thing, but it lets you better choose how to craft an API.
Hmm, kind of grows on you!
Implemented by #2592, without underscores
Most helpful comment
This solves the Ruby issue with kwargs. Often the calls read well, but then you're stuck with variable names that mean nothing. For example
Comment.moderated(by: user)
is great, butby
is terrible internal naming.Reassigning is a solution but not a pretty one. I think like the proposal, kinda alien, but it reads damn well. To be fair I must admit my first reaction was: WTF.