4 ms·
Hmm. Why do you think they would lead to misuse via functions that take too many parameters? I understand how this is a problem but I’ve seen more than one fun
by mucholove 6y ago
Hmm. Why do you think they would lead to misuse via functions that take too many parameters?
I understand how this is a problem but I’ve seen more than one function in C that takes 4 or more parameters.
Methinks it’s more a product of being an inexperienced coder and not being able to model a domain appropriately. The huge benefit? If you do need 4 parameters you can at least read them / understand them if they are named!
Would love to see you flesh this out a bit more
- gumby 6y agoWell, my example was a small case of this problem. It’s not unreasonable that when operating on floating point, different algorithms can need different ways of determining which FP number to choose when the result cannot be precisely represented. And the example I gave is innocuous...though it’s likely an underlying function called by functions that implement higher level semantics. And in fact in that case using a keyword isn’t that different from binding the meaning into the function name (`divide_rounding_down` and so on). But in my experience I saw a pernicious pattern: that the function name would essentially become the common entry point for a large number of divergent functions `divide(dividend: x, divisir: y, truncate: true, underflow_handling: ufh::throw, negative_permitted: true)` and so on (contrived example for explanatory purposes). In addition, positional arguments are simply easier to read; as keyword arguments are not ordered, you’re essentially parsing the arguments, seeking the important ones etc. You’re increasing the cognitive overhead of the common case while only marginally assisting the uncommon case. It’s like reading while hearing the sound of the words in your head: quite possible, but significantly slower than having the meaning enter directly.
- mucholove 6y agoNot a minor nit: in Objective-C, keyword arguments are ordered. Every function call reads like a sentence and the keyword argument itself can be something phrase like: -onStream:writeObject:withKey:;
- jasode 6y ago>Also they can lead to a poor pattern which is functions/methods that take too many parameters. >In addition, positional arguments are simply easier to read; as keyword arguments are not ordered, you’re essentially parsing the arguments, seeking the important ones etc. Microsoft's raw Win32 API SDK is based on the C Language and even though that language doesn't have named parameters, it still did not discourage functions designed with lots of parameters that's difficult to mentally parse. To make such code self-documenting, this is how I typically write Win32 API calls to make the code readable: ret = CreateProcess(NULL, // _In_opt_ LPCTSTR lpApplicationName, cmd, // _Inout_opt_ LPTSTR lpCommandLine, NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, NULL, // _In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, TRUE, // _In_ BOOL bInheritHandles, CREATE_NO_WINDOW, // _In_ DWORD dwCreationFlags, NULL, // _In_opt_ LPVOID lpEnvironment, NULL, // _In_opt_ LPCTSTR lpCurrentDirectory, &si, // _In_ LPSTARTUPINFO lpStartupInfo, &pi); // _Out_ LPPROCESS_INFORMATION lpProcessInformation If the language had named parameters, I wonder if Microsoft would have used them so programmers wouldn't have to sprinkle extra comments like that. (Comments are also brittle because they're not real tokens that the compiler would check.) Without the comments, it looks like this: ret = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, NULL, &si, &pi); All those naked NULLs and TRUE params are very hard to parse unless you've memorized the positions. I'm not recommending that we need the verbosity of Objective-C named parameters but I don't see how mentally relying on position is less cognitive load.
- gumby 6y agoI don’t recommend a large number of parameters in any paradigm. Your case is a good example.
- jasode 6y ago>I don’t recommend a large number of parameters in any paradigm. Your case is a good example. Sure, I understand that too many parameters is to be avoided and can point to some underlying poor architecture design. However, _if_ there's irreducible complexity that calls for 5+ parameters to minimally specify behavior, what are the better syntax options than named parameters? If you say "create a struct with members and pass the struct instead", then you're just shifting the "named params" to the "struct member names". This causes the extra verbose syntax of defining one-time-use struct variables to pass as a function argument. In contrast, named parameters in the function can act as "anonymous unnamed struct" which is cleaner. Using structs as function parameters is more useful when the structs can also be re-used elsewhere in multiple places (e.g. 3d points {x, y, z}).
- a1369209993 6y agoBecause most people can figure out that: CreateProcess(NULL,cmd,NULL,NULL,TRUE,CREATE_NO_WINDOW,NULL,NULL,&si,&pi); is a terrible interface that we should try to split up better, but CreateProcess( lpApplicationName: NULL, lpCommandLine: cmd, lpProcessAttributes: NULL, lpThreadAttributes: NULL, bInheritHandles: TRUE, dwCreationFlags: CREATE_NO_WINDOW, lpEnvironment: NULL, lpCurrentDirectory: NULL, lpStartupInfo: &si, lpProcessInfo: &pi, ); tends not to attract the same scrutiny for some reason. On the other hand, removing named parameters doesn't actually fix this (see jasode's example at https://news.ycombinator.com/item?id=24401913 https://news.ycombinator.com/item?id=24401913 so I don't need another dozen lines), so it's not clear that this is a good argument against named parameters.
- bfrydl 6y agoIn my opinion, the first is a terrible interface because I have no idea what any of the parameters do, not because there are so many. The second is a good interface to me because all of the values are labeled. I don't see any problem with having this many parameters when all of those parameters are relevant (see: Vulkan). Named parameters often implies default values too, which means you wouldn't have to specify them all. I basically emulate this in other languages, like Rust, with an “options” struct that has default values.