Post #3955849
2026-07-19 19:09 UTC
A design feature I generally dislike in any programming language is when the name of keyword arguments necessarily matches the name of the variables referring to them in the function body.
Consider a positional argument of a function "f". When I call "f(data)", it doesn't matter what the variable here called "data" is called (in fact, it can even be a complicated expression).
In the definition of "f", I may write something like "function f(input) …". Now, again, it doesn't matter what variable name I use for that argument.
However, for a "named argument" or "keyword argument", often, the name of the variable in the function definition matters at the call site. For instance, I might have to write "f(input: data)".
This is bad! Variable names are arbitrary! It is senseless to make them subtly meaningful in a small minority of circumstances!
Unfortunately, almost every language does this. I only know of one popular language that doesn't: Swift. And languages which do not have keyword arguments as a built-in feature, but emulate it, like JavaScript.
Obviously, you can just rename the variables inside the function manually by declaring a new variable. I think that this should be the default, and you should be required to write out the name twice if you want the name to be the same.
Overall, there are three names, and one of those, the one in the interface, is the only one both sides of the interface should have to know. I define "f(x: y)" and this is called as "f(x: z)". Z becomes X (the interface name) which becomes Y.
But… actually… this principle should apply more generally. I think at no point should ever be this coupling across an interface. Unless it's really necessary for important run-time reasons.
An unimportant example of this is modules. If I define a module/package and export a function named "foo", it will be available as "foo" to importers of the module. This is also bad! I should have to explicitly state that it just so happens the internal name of the function matches the interface name.
A more important example of this is traits/interfaces/typeclasses/constracts/protocols (there are a lot of names for them). In this case it is very important to be allowed to change the name, because traits are often implemented as an afterthought. And because for types without traits needing implementing, we rarely think that the method names are important.
Rust gets this right: I can separately define "Data::encode" and "<Data as Encodable>::encode". The function "encode" doesn't automatically become the implementation of the trait function just because of its name.
Just to reiterate: This is how it should look!
module Foo {
export foo = fn foo(data: data) {
...
}
}
#swift #rustlang #programming
Replies (0)
No replies.