We Have Named Arguments at Home

45 points by itamarst


giacomo_cavalieri

What a nicely written article

friction produces better APIs

Couldn’t agree more! I’m all for languages having a smaller set of features rather than being ever-growing

ancienthero

I used to be in the camp for keyword/optional arguments, but thinking about it more I've changed my mind completely. After seeing the implications of making parameter names part of the public API, I don't think the complexity of expressing that is worth it. Not to mention, the default values of optional arguments are also practically part of the public API, but that fact is hidden almost by definition.

I tend to agree that it's enough to semantically group related parameters into their own type. It makes writing callsites inconvenient, but isn't it nice to look at a function call and be able to intuit what it's doing, even if it's verbose?

It's true that people won't naturally do this, especially for functions with just a few parameters. But I think the reverse is true too, where keyword arguments passively encourage people to write god functions that force you to read the docs anyways.

yosefk

"All of the above might be useful, though localized, syntax improvements. But the hidden tax is that the language becomes more complex, for arguably little gain." -- you can't seriously say this about Rust, a very complex language; the extra complexity isn't much compared to what's already in there. The gain is IMO very real - you get a nice API without it becoming a design problem (which keeping the "complexity" out of the language makes this into) for half the functions you write (the post says this is actually an advantage, in practice instead of thoughtful workarounds creating generally useful types you will simply get bad APIs with ugly callsites most of the time)

nytpu

I broadly agree with this (especially considering the conclusion that thinking about restructuring the API is better than poorly emulating these features in the first place) although it somewhat feels like making excuses for "why this design you don't like and can demonstrably be done more conveniently is good actually". But like a lot of annoyances in programming languages, Rust especially it seems, I fully understand why it's like that even if I abstractly wish it didn't have to be like that.

olliej

Back when I was first implementing restructuring assignment in JavaScript I realised that it made named arguments* really nice:

function f({x, y, z}) { … }

That said I’m not super keen on the struct based approach in strongly typed language as it means every function starts needing its own type specified. I don’t think the argument that the type belongs to your type(s) is particularly compelling: the type you’re defining is a property of the specific interface.

E.g let’s say you have two functions both of which reasonably have the same parameter names? Should they be duplicated or shared?

The arguments for types like size isn’t super strong - a size or a rectangle/bounds is a reasonable thing to have as a standalone type.

carlana

I tend to agree with this. Most of it applies to Go too, except for the part about default structs. It adds friction, but in a static language a little bit of friction can be good.

wrs

I do many/most of these things all the time. Is that not normal? Actually, when I first opened Steve’s article I expected it to read more like this one!

junot

My IDE that shows me parameter names at any call site. Whatever "friction" the author is depending on to enforce their idea of good taste isn't there with the right tooling. It would still be nice if Rust directly supported named parameters.

In both posts, it also seems assumed that if Rust gets named parameters then argument defaults and function overloading are inevitable features. I don't think that's correct.

I also doubt the compiler treats passing ownership of a struct to a function the same as passing the unwrapped arguments, so the struct-based workaround is not only added boilerplate to achieve a syntactic sugar that should be more directly supported, but it probably also has minor side-effects on code generation.

yshui

I wish Rust had Zig's inferred struct name. i.e. this:

let cropped = image::imageops::crop_imm(
    &img,
    Crop {
        x: 10,
        y: 20,
        width: 200,
        height: 100,
    },
);

becomes


let cropped = image::imageops::crop_imm(
    &img,
    _ {
        x: 10,
        y: 20,
        width: 200,
        height: 100,
    },
);
MatheusRich

I've written something similar years ago https://thoughtbot.com/blog/rust-doesn-t-have-named-arguments-so-what

schneems

An optional argument is, to some extent, an argument which may or may not exist. Rust has a type for that.

A thing I’m missing is to have a mix of required and optional fields in a struct. When you mark it Default ALL fields must provide a default and that also makes those types less useful (fewer guarantees). Or you end up having to wrap things in options as an extra bit of boiler plate on every invocation.

Maybe a PartialDefault trait that allowed for omitting some fields could be generally useful. It would err out if you tried to construct a struct missing a required field but otherwise fill in defaults for other ones.