5 ms·
N-Params vs. Single Param
- disillusionist 2y agoI 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 });
- carlos-menezes 2y agoLikewise. Even if the input object only has a single property, I'll still use this pattern.
- disillusionist 2y agobecause i'm likely going to refactor this function "tomorrow" with additional properties. :)
- avandekleut 2y agoAnother common pattern is to put the "primary" argument first, and the rest in an "options" object argument.
- carlos-menezes 2y agoIt works fine when your function takes a single primary argument—like `getLastWord(word: string, options?: TGetLastWordOptions): string | undefined`. The function name makes the purpose clear, and with just one main argument, it’s easy to follow. Sure, you're still "guessing" what the first parameter is but it’s not a heavy mental lift. You could argue that as long as you're consistent, using positional arguments in binary functions is fine–the pattern is clear and predictable. But in the example from the OP, there isn’t really a primary argument. You're dealing with multiple values that carry equal weight, which makes positional arguments harder to reason about at a glance.
- hyperhello 2y agoWhat 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).
- geakstr 2y agoSome 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.
- hyperhello 2y agoNeither really addresses the issue. Making a type for a single kind of string seems like an abuse of types just to shoehorn the documentation in. Documentation can be used directly of course, but that moots all of this -- just document? Yeah, but naming the variable is super quick compared to either.
- oneoverten 2y agoVariable names is just documentation. Having types that can assert some condition on the underlying value is not even comparable to "having a named return variable". (just document? just name variables?) You don't care about what the name of the returned value is, you care about what *it is*.
- avandekleut 2y agoTypescript nukes primitive aliases like this sadly. Intellisense will just infer it as "string".
- disillusionist 2y agoyou 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' });
- 9d 2y 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 2y agoIt’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.
- hwpythonner 2y agoNot 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 2y agoI 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 2y ago[deleted]
- dicytea 2y agoDoesn't seem to be a problem, at least for Rust: https://godbolt.org/z/fToxz3d7a https://godbolt.org/z/fToxz3d7a. Functions with plain arguments and a struct both produce identical assembly output.
- carlos-menezes 2y agoC++ for reference: https://godbolt.org/z/7G6qxK6T8 https://godbolt.org/z/7G6qxK6T8
- hwpythonner 2y agoTry compiling with optimizations. I think by default this site doesn't add optimization flags. Here what happens with optimizations: https://godbolt.org/z/G18zd7chP https://godbolt.org/z/G18zd7chP Look at the registers usages vs stack
- tantalor 2y ago"Avoid the Long Parameter List" https://testing.googleblog.com/2024/05/avoid-long-parameter-list.html https://testing.googleblog.com/2024/05/avoid-long-parameter-...
- joeyagreco 2y agoIn my opinion, this is the _only_ way to handle n-param functions in production code.
- sixo 2y agoA 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-type-definitions https://samkrit.ch/2025/03/09/programming-1.html#anonymous-t...
- feoren 2y agoIsn'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 2y agoI 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 2y agopython 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)
- krystofee 2y agoThis is just because TS doesnt have keyword or keyword-only arguments as Python for example has.
- mx-bojangles 2y agoI 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 2y agoIf 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.
- simianwords 2y agoThe other advantage to this pattern is that you can create a generic interface out of this. With the N-params pattern it is not possible to create an interface on this method because the types of each param may be different across different implementation.
- Joker_vD 2y agoHere 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 2y agoIt’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 https://wiki.ralfbarkow.ch/view/parameter-object