Readit News logoReadit News
disillusionist · 5 months ago
I started using this pattern years ago and haven't looked back. React components are defined using a single properties param and it feels natural to extend the practice to other methods and helper functions that I write.

One nice unexpected side effect is that I end up with more consistency in variable naming when using a param object in order to benefit from object definition shortcuts.

ie I will usually write something like

  const firstName = getFirstName(); doTheThing({ firstName });
rather than

  const fName = getFirstName(); doTheThing({ firstName: fName });

hyperhello · 5 months ago
What bothers me a lot is that return values can't have a name. Suppose I have a function

string combineName(string first, string last);

Okay, I assume the result is the full name, but I don't really know that like I would if the return had a name in the header. (The function can be getFullName, yes, but that's so simple it doesn't matter).

disillusionist · 5 months ago
you can do this rather easily by returning an object rather than a primitive. if you're using a language like TypeScript, destructuring the resulting returned object is rather trivial and (in my opinion) delightful to read. eg

  function combineNames({ first, last }) {
    const fullName = `${first} ${last}`;
    return { fullName };
  }
  const { fullName } = combineNames({first: 'John', last: 'Doe' });

geakstr · 5 months ago
Some languages allow to define type alias, name of this type can be self-explanatory:

  type FullName = string;
  function combineName(first: string, last: string): FullName;
Also documentation comment for function is what specifically solves this - describes what function does and returns.

avandekleut · 5 months ago
Similarly to passing an object argument, you can return an object.

Then pair it with destructuring assignment!

`const { fullName } = getName({ firstName, lastName )}`

wvenable · 5 months ago
I fail to see the distinction, for documentation purposes, of this versus just giving it that `getFullName` function name.

A more complex example will probably return a more complex type that is more self-documenting. If the type is a single scalar value then I don't see the value of adding another name here.

mpweiher · 5 months ago
Name the function/method as the thing it "returns".

In fact, just forget that it is a function that does something. Treat it as the thing it returns.

string fullName( string firstName, string lastName )

carlos-menezes · 5 months ago
Likewise. Even if the input object only has a single property, I'll still use this pattern.
avandekleut · 5 months ago
Another common pattern is to put the "primary" argument first, and the rest in an "options" object argument.
disillusionist · 5 months ago
because i'm likely going to refactor this function "tomorrow" with additional properties. :)
hwpythonner · 5 months ago
Not an expert in JS, but maybe this makes sense in JS where everything is passed as objects anyway and having a single param is often more readable there.

But in native languages like C/C++/etc, this pattern usually hurts performance. Separate arguments go into registers (faster but depends on number of arguments), while structs often involve indirection, and sometimes alignment/padding issues. Also, passing a const struct doesn’t guarantee the fields inside are truly const.

That said, it's context dependent. If you're passing stuff through multiple layers or want a more stable function signature, using a struct can be cleaner. Just not something I’d generalize to any language or any use case.

taeric · 5 months ago
I think the dream has long been that a compiler could optimize away any inefficiencies that can be mechanically described. As such, if there is a benefit to seeing the same structure in many places, using a struct would be ideal.

I don't think we are near said dream, yet.

Deleted Comment

dicytea · 5 months ago
Doesn't seem to be a problem, at least for Rust: https://godbolt.org/z/fToxz3d7a.

Functions with plain arguments and a struct both produce identical assembly output.

carlos-menezes · 5 months ago
airstrike · 5 months ago
Also worth noting clippy will warn about writing functions with too many arguments for the same reason listed in TFA

https://rust-lang.github.io/rust-clippy/master/index.html#to...

physicsguy · 5 months ago
If you add a third argument you get different assembly
the_other · 5 months ago
I’ve seen some evidence it hurts performance in JS, too. If you’re doing something “a lot”, like media qstreaming, or realtime updates, ordered params are likely faster.

I agree with others that the destructuring is a nice syntax to read and write. Yet, I have started to feel it’s an overused pattern, deployed by habit rather than consideration (in my code anyway). I can’t put my finger on why. It’s possibly a gut-feel that my functions would be better taking feeer arguments.

oneoverten · 5 months ago
Yes. This is true for JS too, object-wrapping parameters just to make them named will add runtime overhead.
fenomas · 5 months ago
Not in current chrome, at least for simple cases (like a short function with a single argument). It benchmarks the same with or without the param wrapper.
sixo · 5 months ago
A little while ago I sketched some ideas for a programming language [1], one of which was that I would like a language who did not distinguish between these two cases. Obviously you can always write a function with a single N-param argument, but it's rather verbose, but would be nice to also be able to write the param list normally and then refer directly to its parameter type elsewhere. Although this would require that the "set of possible parameter lists" were the same as "the set of possible object type", which isn't the case in any language I know of.

[1] https://samkrit.ch/2025/03/09/programming-1.html#anonymous-t...

feoren · 5 months ago
Isn't C#'s named arguments basically what you want? You can choose to use the names or not at the time of calling the function. You can name some arguments but not others. Don't many programming languages have this feature?
intrepidhero · 5 months ago
I had a similar thought while designing my dream programming language but I also wanted automatic currying and I couldn't figure out an ergonomic way to have both.

Now I'm gonna go read your blog series.

ashtuchkin · 5 months ago
python does this pretty elegantly:

    def my_func(a: int, b: int):
        print(a+b)

    # both of these can be used
    my_func(1, 2)
    my_func(a=1, b=2)

ImageXav · 5 months ago
Even better, python has named tuples [0]. So if you have a tuple that you are sure will always have the same inputs you can declare it:

``` Point = namedtuple('Point', 'x y') pt1 = Point(1.0, 5.0) ```

And then call the X or Y coordinates either by index: pt1[0], pt1[1], or coordinate name: pt1.x, pt1.y.

This can be a really handy way to help people understand your code as what you are calling becomes a lot more explicit.

[0] https://stackoverflow.com/questions/2970608/what-are-named-t...

sixo · 5 months ago
This isn't what I'm talking about. I would want

    def my_func(a: int, b: int):
      ...

    def my_func2(args: my_func.__args_type__):
      ...
with `my_func.__args_type__` standing for an object like `{a: int, b: int}`.

ziofill · 5 months ago
Also `my_func(**{a:1, b:2})`
9d · 5 months ago
> No guessing. No worrying about the order. Your code is self-documenting. Plus, TypeScript gives you full autocompletion and type safety.

1. Most of the time, arguments are not the same type, so if you're using TypeScript, you're already getting errors at this point. In your example, only first/last names might get mixed up. And if you still get those wrong while writing that code, you're just phoning it in atp and maybe should take a day off.

2. The same TypeScript that checks your types, also lets you see the signature when hovering over the function.

In real world coding, this isn't an issue, and you'll probably give up keeping this "rule" after a year or two. But good that you're at least thinking about these kinds of things.

carlos-menezes · 5 months ago
It’s a preference grounded in long-term ergonomics that work well for me. I’ve been using this "rule" for over two years now and haven’t found a reason to give it up.
mx-bojangles · 5 months ago
I agree that the example is easier to read with the single object param. It might be even easier to read using a fluent interface or a builder pattern. You should choose the best tool for the job.

As a counter-example, I prefer

    setEnabled(item, true);
to

    setEnabled({item: item, isEnabled: true});

janalsncm · 5 months ago
If I have five parameters and they all need to be set (otherwise the object is invalid), think of a constructor as a sort of builder pattern that does all five at once and avoids checking for invalid states (e.g. you only set 3 of the variables) in other methods.
tantalor · 5 months ago
Joker_vD · 5 months ago
Here is a case were object parameters aren't the better choice:

    type TBinarySearchParams<T> = {
        needle: T;
        haystack: [T];
    };

    const binarySearch = <T,>(args: TBinarySearchParams<T>) => { ... }

    binarySearch({needle: 42, haystack: [1,2,3]});
Or at least, I would argue they are not better, but if you still insist:

    type TIsEvenParams = {
        num: number;
    };

    const isEven = (args: TIsEvenParams) => { ... }

    isEven({num: 7});
Surely that is strictly worse?

jiehong · 5 months ago
It’s known as the parameter object pattern[0].

A draw back would be is you want to overload such a method with fewer parameters, it then becomes weird.

[0]: https://wiki.ralfbarkow.ch/view/parameter-object