7 ms·
Maybe it's shallow of me, but named/keyword arguments are now at the top of my list of things I want in a programming language. For all the attention given to
by kiawe_fire 6y ago
Maybe it's shallow of me, but named/keyword arguments are now at the top of my list of things I want in a programming language.
For all the attention given to static typing and unit testing for increasing developer confidence, I think named arguments belong up there as well. Makes reading code so much easier, especially the way they are implemented and encouraged in Swift.
As someone stuck doing PHP for my day job, they could buy an awful lot of goodwill from me by implementing something like this in PHP (well, that, and separating arrays from dictionaries, but that seems far less likely to happen).
- MrBuddyCasino 6y agoAutomatically generated builders are also nice, not sure yet which I prefer.
- oefrha 6y agoBuilders are nice when you actually need to build something with tons of optional arguments. The vast majority of cases where named arguments shine are function/method calls where builders just don’t make sense. Also, named arguments complement/augment the builder pattern.
- MrBuddyCasino 6y agoGood point. Also, named arguments can be compiler checked for mandatory values, with builders the error happens at runtime.
- swsieber 6y agoYou can actually have type safe buildrs in rust where trying to call the method that consumes the builder fails to compile if not all the arguments are set correctly. https://github.com/idanarye/rust-typed-builder/blob/master/README.md#rust-typed-builder https://github.com/idanarye/rust-typed-builder/blob/master/R...
- MrBuddyCasino 6y agoAh yes, linear types to the rescue! Nice.
- swsieber 6y agoI think technically it's affine types. In this particular case it comes from really nice generics - you start out with a struct that's generic over all fields - one generic bound per field. And that let's you implement methods depending on what fields have been set. So you could even make a builder with mutually exclusive setters. But you'd probably just want to use an enum in that case for the mutually exclusive options.
- MrBuddyCasino 6y agoI still don't quite understand the generics magic required in order to not generate exponential amounts of code for it to allow random order of arguments, but I won't complain. That capability would have come in handy on Java/Lombok.
- swsieber 6y ago> exponential amounts of code for it to allow random order of arguments The key is that it only generates code for types that actually get used. So if you always call the orders in the same order, it won't generate tons of code. That said, you could actually tweak the type-safe builder to enforce a certain order for calling the methods on the builder. Also, it's not the order of arguments, it's the order of method calls :)
- kevincox 6y agoI have long envisioned a language where these two things were tightly combined. The rough ideas are: - All arguments are keyword arguments. - Each function implicitly defines a struct type equivalent to its arguments. I think if you combined this with some nice structural typing you can get a lot of the argument forwarding/builders and similar that people enjoy in dynamic languages without much/any overhead. Rough example: fn add(a: int, b: int): int { a + b } fn add3(c: int, rest: add::args): int { add(rest) + 3 } A somewhat silly example but I think you can see the point. Of course you could also add language-level builder syntax to these structs and have something really nice. I think the key here is that once you make the arguments a static type you can add loads of convenience like builders very easily.
- wwright 6y agoThis makes me wonder if there is some sort of relationship between currying and row polymorphism; that could be pretty sweet to capture succinctly in a language
- eeereerews 6y agoI've thought about this too, but keeping positional args. The basic data type would be a "blob", consisting of a struct part and a tuple part.
- MrBuddyCasino 6y agoArgs reified as structs, interesting! On the other hand, not exceptional at all when looking at it from a Lisp angle.
- simias 6y agoI like named/keyword arguments too but I think one needs to be careful not to overdo it, especially in statically typed languages. I think it's sort of a last resort when the call syntax can't be disambiguated by other means. As an example imagine a search function that returns the position of a character `c` in a string `str` that looks like that: int search(String str, char c, bool case_sensitive, bool from_end) I'd say that I would avoid this signature in general, because those two `bool`s make for an awkward call syntax: search("foo", 'f', true, false) That's pretty opaque, even if you know what the two options are it's easy to forget the order. Now you might say "well see, that's where keyword parameters shine!" and you'd be right, it does help a lot: search("foo", 'f', case_sensitive: true, from_end: false) But I'd argue that while this is good enough if you're coding in a dynamically typed language, it's actually sub-obtimal if you're coding in Rust for instance. Here you can do something even better: give these two options different types, this way not only it makes the code more explicit, it also means that it gets enforced by the compiler: enum SearchCase { Sensitive, Insensitive }; enum SearchDirection { FromStart, FromEnd }; search("foo", 'f', SearchCase::Sensitive, SearchDirection::FromStart); Now that's not to say that I don't want or like keyword arguments, I just think that it's one of these situations where "when you have a hammer, everything looks like a nail". I sometimes miss keyword arguments in Rust, but it's the exception rather than the rule for me.
- oefrha 6y agoYou’re forgetting another important use case of keyword/named arguments: default values. In Rust either every call site needs to specify the default values, or you use a None for the default which is not at all readable. Also, if Rust were to support named arguments (even better if with default values), you can use explicit types for your bools all the same. It’s not an either or situation.
- kbenson 6y agoWouldn't a struct for named options that have defaults which is passed as the last option emulate optional named arguments with default values?
- mariodiana 6y agoIf they could just add square brackets, it would be perfect.
- saagarjha 6y agoI think you might be interested in a little language called Objective-C ;)
- frou_dh 6y agoA sufficiently smart editor could show names in front of positional arguments, even if named arguments are not part of the language.
- skohan 6y agoTools are great, but I'm not a fan of the idea of needing special tooling outside of the source code itself in order to be able to understand it. Maybe it's just me, but I would hate to imagine living in a world where my projects might be incomprehensible if I ever lost my .vimrc
- frou_dh 6y agoIn this case it would be a pure value-add, because the fallback is the way everyone can evidently cope with anyway.
- adrianmsmith 6y agoI'm not sure that's entirely true. If a large proportion of people use a more advanced development tool, it sort of becomes indispensable, because people design the software to be edited with the tool, such that it becomes difficult to edit it without the tool. For example if, today, I checked in code like performRequest(request, response, null, null, null) you probably wouldn't consider that good code, as it's not clear what the nulls mean there. Most people today would try to not write code like that. But if everyone was using an editor which displayed the parameter names, then it'd be fine. People would write that code, check it in, and so on. Which would mean that those people without the tool would have to confront such code.
- adrianmsmith 6y agoIntelliJ does this in Java. It only does it for those arguments where the meaning could be unclear. For example, true/false or null.
- anchpop 6y agoRust-analyzer does this, and I find it much more helpful than I thought I would
- ndotl 6y agoNamed arguments are great but should be used with care as they also create a reverse dependency. It prevents you from ever renaming the arguments in the future.
- saagarjha 6y agoJust like you can't ever rename the function in the future?
- paublyrne 6y agoIn the case of a library that would make this a breaking change you could add the new key rather than changing it, and support both while flagging old key usage with a deprecation warning.
- fsloth 6y agoWhy would it? When you change the name of nth argument from foo to bar, compile will fail and then you just need to do a global rename. Is there any scenario where this would not be feasible?
- wool_gather 6y agoThe caller can't do a global rename if they don't own the source of the function -- because it's in a library. Which is what parent meant by "reverse dependency": the library's interface is constrained by the fact that it has users.
- DagAgren 6y agoBut this is already the case for the name of the function. Semantically, named arguments are just part of the name of the function.
- fsloth 6y agoSurely if the library interface changes the users can change their own code? That's why we have versioning of libraries - so that users of the library can take intelligently new versions into use, and especially so they are aware if (and when) the interface changes.