[HN Gopher] Get rid of those boolean function parameters (2015)
___________________________________________________________________
Get rid of those boolean function parameters (2015)
Author : melzarei
Score : 55 points
Date : 2021-09-20 08:57 UTC (14 hours ago)
(HTM) web link (mortoray.com)
(TXT) w3m dump (mortoray.com)
| bjourne wrote:
| Named parameters misses the point. Functions should do one thing
| and only one thing. This is why we have cos, sin, and tan and not
| a universal function for trigonometry: trig(mode='cos', ...).
| Such functions often become cumbersome to use since they are
| essentially multiple functions baked into one.
| treve wrote:
| Religiously splitting functions with boolean arguments doesn't
| always result in more maintainable code.
|
| Instead of trigonometry functions, how would you refactor JS's
| fetch() with many of its behaviour-altering flags?
| bjourne wrote:
| Good rules of thumbs are given in the "Deciding which to use"
| section of the article. For the fetch() function, I'd keep
| most parameters as is since they don't change the essense of
| the function. But "cache" and "redirect" do (following
| redirects can cause N http requests rather than just one and
| using the cache perhaps 0) so I'd refactor them as new
| functions. Imagine adding retry functionality to the fetch()
| function using parameters. I think you can see how this leads
| to feature creep.
| peoplefromibiza wrote:
| > how would you refactor JS's fetch()
|
| as @flavius29663 said
| (https://news.ycombinator.com/item?id=28593669) you can use
| the builder pattern FetchBuilder()
| .withUrl(ur) .withMode("cors")
| .withCache(true) .withHeader('Content-Type',
| 'application/json') .accept('*/*')
| .post() .then(response => response.json())
| .then(data => console.log(data));
| kevincox wrote:
| I see the builder pattern as a way to manage lack of
| keyword arguments. I see very little difference between
| your example and the actual fetch API that takes an object
| as JS's version of keyword arguments.
|
| Languages with good support for named/keyword arguments
| have more features such as required arguments and
| preventing duplicate arguments. With builder patterns your
| only real option is to make the builder constructor have
| required arguments (or throw a runtime error upon
| finalizing the builder).
| peoplefromibiza wrote:
| > . I see very little difference between your example and
| the actual fetch API that takes an object as JS's version
|
| true
|
| the only difference is in the tooling
|
| code completion for methods names works much better than
| autocompletion for objects' fields.
|
| And you can't mistype a method name, it would not run and
| give you back a - hopefully - meaningful error, while the
| same is not true for objects' fields.
| kevincox wrote:
| > code completion for methods names works much better
| than autocompletion for objects' fields.
|
| That is true for vanilla JS, unknown parameters will be
| ignored and unset will be set to undefined. However for
| languages that support keyword arguments (or even
| TypeScript[1]) the tooling should be even better than for
| the keyword argument case.
|
| [1] https://www.typescriptlang.org/play?#code/GYVwdgxgLgl
| g9mABMO...
| zamadatix wrote:
| I'm not following how moving the options out of the
| function parameters and into the call chain makes the
| actual function more maintainable. It's still doing the
| exact same thing with the exact same options it's just
| pulling them from elsewhere. If anything you now have more
| functions to maintain on top of the function that does many
| different things based on the calling info.
| // The original "misses the point" trig(mode="cos",
| type="hyperbolic") // The style fetch()
| uses today trig({mode: "cos", type: "hyperbolic"})
| // The builder refactor trigBuilder().withMode("Cos
| ").withType("hyperbolic").calculate()
| silvestrov wrote:
| function pizza(boolean pepperoni, boolean bacon, boolean
| mushroom, boolean artichoke)
|
| now becomes 16 distinct functions.
| flavius29663 wrote:
| pizza is very suitable for the builder pattern: CreatePizza()
| .WithBacon() .With(artichoke) .Build();
|
| or any combination of the above: CreatePizza()
| .WithMussroom() .Build()
|
| Even better, you can add new ingredients without changing any
| of the existing signatures: CreatePizza() .WithProsciuto()
| .WithTomatoSauce() .Build()
| stnmtn wrote:
| What is the difference of this and doing an object
| paramater, similar to JS?
|
| createPizza({bacon: true, artichoke: true});
|
| This has the same benefit you describe of being able to add
| parameters without altering any old call sites
|
| createPizza({prosiuto: true, tomatoSauce: true});
| fendy3002 wrote:
| Not OP, but it's useful for if:
|
| - something is done to object when some props is set or
| validation, such as .withStuffedCrust("cheese"), it'll
| set the internal props as crust="stuffed" and
| stuffContent="cheese"
|
| - it's branching. So rather than making user looking for
| the components or configuration themselves, library
| author can guide them with builder. Such as:
| myVehicleBuilder.withSixTires().withTrailerAttachment().a
| ttach(container)
|
| In this case withTrailerAttachment (and possibly
| withOpenBack or withBox) won't show up if you call
| withTwoTires(), and attach won't show up if you don't
| call withTrailerAttachment().
| flavius29663 wrote:
| In my example the could matter, or not. I see this all
| the times. In your example, can you make the order
| matter? In my example, after each selection, you can
| limit or expand the further options.
| CRConrad wrote:
| Nothing to stop you from doing the same within the code
| of the multi-parameterized single function, is there?
| if Shrimp in Fillings then begin Add(Shrimp);
| Add(ShrimpOil); // So yummy together
| Fillings.Remove(Jalapenos); // Don't go together
| end; if Jalapenos in Fillings then begin
| Add(Jalapenos); end;
|
| etc. No?
| flavius29663 wrote:
| You just moved the problem to a different layer.
|
| In your case you would end up calling your function like
| this: DoToppings(true, false, true, true);
|
| Whereas in my case you would call the builder directly
| with (only) the params you want different than the
| defaults.
|
| You could use named arguments, but that doesn't solve the
| problem completely. You will still have a large method
| signature, hard to use, harder to refactor, harder to
| test, error prone.
|
| It also causes a lot of redundant code: the called
| usually only cares about one or 2 arguments, it's rare
| that you *need* to pass more. The rest of the args are
| filled in by defaults in the implementation. There could
| be elegant ways to handle the defaults, like overloading
| methods or just passing in defaults in the method
| signature. Default value are pretty bad IMO, for all the
| reasons above. Btw, if you *need* to pass that many
| arguments to a function, that is another code smell worth
| it's own discussion.
|
| Multiple overloaded methods could have about the same
| amount of code int he implementation like the builder
| pattern, but they have a huge drawback: the caller cannot
| mix and match which arguments they want to pass in. If
| you have 4 arguments, there would be quite a high
| combination of parameters (18 possibilities? - 18
| functions); Using the builder pattern you have to
| implement 4 methods only, and you're covering all the
| possible combinations the client might want. You can also
| limit some combinations in elegant ways right in the IDE
| while the developer is writing the code.
|
| Think of FluentAssertions
| https://fluentassertions.com/introduction They have
| literally hundreds of possible assertions that are
| represented by object instances. You can combine them in
| an almost infinite number of possibilities.
|
| Sure, there is no black and white, and depends on the
| language, the builder pattern is a good tool to have in
| the toolbox.
| peoplefromibiza wrote:
| not necessarly.
|
| First of all, this function pizza(boolean
| pepperoni, boolean bacon, boolean mushroom, boolean
| artichoke)
|
| breaks down when you want to add ham, potatoes and sausages
| to the pizza.
|
| Secondly, you can optimize for the common case:
| fn pizza() # -> default pizza e.g. margherita fn
| pizza(list_of_ingredients) # -> your custom pizza
|
| if you we are talking of simple functions and not more
| complex patterns, such as piping, in Elixir I would do
| pizza |> add_ham |> add_mushroom |> well_done
|
| when using boolean parameters you are also passing down a lot
| of useless informations (a pizza with pepperoni would include
| 3 false just to remove the ingredients from the pizza and
| only one true) and confining yourself to a fixed set of
| options, that could possibly also represent an impossible
| state.
| stephenr wrote:
| What kind of monster puts _potatoes_ on a pizza?
| PretzelPirate wrote:
| I love potatoes on pizza and order it at multiple pizza
| places. They add flavor and creaminess. The secret is to
| cook the potatoes properly and not just throw some french
| fries on the pizza.
| [deleted]
| junon wrote:
| pizza_with_pepperoni_and_bacon_and_mushroom()
|
| Wow glad I took out those boolean params!
| flohofwoe wrote:
| A valid use case for functions with many arguments are
| setup/init/creation functions which sometimes take dozens of
| configuration options. It's definitely better to do such setup
| operations in a single 'atomic' function call than spreading
| this out over dozens of configuration functions where it isn't
| clear when and in what order those should be called, or a
| combinatorial explosion of atomic setup functions, one for each
| valid combination of setup options.
| dragonwriter wrote:
| In a language with only ordered arguments, sure, boolean
| arguments are generally unreadable, but so is more than one
| parameter generally unless they are logically equivalent (the
| arguments to an add function), following a convention from some
| other context (e.g., the arguments to an divide or subtract
| function), or each unique in type in a way that they could only
| have on relation to the function (e.g., the iterable and function
| arguments to a map function; you may have to work to remember the
| order when writing, but when reading the meaning should be
| clear.)
|
| With keyword arguments, this problem goes away, and not just for
| boolean arguments but for arguments generally.
| btbuildem wrote:
| This reminds me of a time I worked with Typescript, and the team
| kept reopening discussions around the use of the "Any" type.
|
| Trying to mitigate the boolean param dilemma, I would lean on
| Erlang and its multiple function signatures. It tends to force
| your solutions into initially harder but eventually more graceful
| form.
|
| Generally, when my code starts showing these kinds of warts
| (silly parameters getting tagged on to function signatures), I
| take it as a sign that the initial tack I've taken no longer
| addresses the problem I'm trying to solve. More often then not it
| goes all the way back to an incomplete / outdated understanding
| of the business logic.
| drath wrote:
| This should really be solved by using named parameters or by
| writing docblocks so that IDE can show hints.
|
| Another trick, at least in js, is to use destructuring
| assignment, e.g. function calc_formula({a, b,
| is_gain}){ ... }
| calc_formula({a:1, b:2, is_gain:true})
| dugmartin wrote:
| My rule of thumb these days in JS/TS is all functions with more
| than 2 parameters should be refactored to a single object
| parameter using destructuring. I don't start with a single
| object because most times YAGNI applies.
| seanwilson wrote:
| > My rule of thumb these days in JS/TS is all functions with
| more than 2 parameters should be refactored to a single
| object parameter using destructuring.
|
| Does this create garbage for the garbage collector (which
| might be an issue for inner loops)?
| kevincox wrote:
| In theory yes. But for hot inner loops you will probably
| get it optimized away. (This is a common pattern so
| optimizer try to find it and undo it. Furthermore hidden
| classes for objects are common and when you destructure
| directly in the argument list escape analysis is pretty
| easy) It probably does hurt your performance for warm and
| cold code but that likely isn't too significant.
|
| So yes, if performance is critical you should probably
| profile and consider avoiding this pattern, but for most
| cases the performance impact is very minor.
| 10x-dev wrote:
| IntelliJ solves this nicely by showing the parameter name at call
| sites, effectively making it look like the language has named
| parameters.
| Sjonny wrote:
| Except this isn't an issue that should be solved at the IDE
| level. Not everybody is using the same IDE and has all the same
| features, or even the same options enabled in an IDE.
|
| The solution provided in the article is the way to go.
| dhosek wrote:
| Although, it doesn't always. For example,
|
| calc_formula(1,2,true)
|
| would show as
|
| calc_formula(a: 1, b: 2, is_gain: true)
|
| but
|
| calc_formula(1,2,some_var)
|
| shows as
|
| calc_formula(a: 1, b: 2, some_var)
|
| Although on the flip side, it creates an incentive for the
| developer to choose sensible names for variables, functions
| etc.
| fxtentacle wrote:
| Fully agree. No need to change the source code to fix what
| isn't broken.
| tsimionescu wrote:
| While I think parameter names at all call sites is the right
| solution, I don't think it's good enough to have this in the
| IDE alone. I still have to review code online that says
| `add(a, true)`, even if IntelliJ showed me `add(a,
| ignore_negatives:true)`.
| 10x-dev wrote:
| The point is that this is a presentation issue. There is
| nothing wrong with the model. It would not be impossible
| for the reviewing software to analyze source code and
| display named parameters.
| CRConrad wrote:
| > The point is that this is a presentation issue.
|
| I think the point was that it shouldn't be.
|
| > There is nothing wrong with the model
|
| Seems to me there is.
| rrobukef wrote:
| OTOH, programming for the human and not for the computer,
| included caring about presentation. I could say there's
| nothing right with the model either - a non-issue,
| personal preference.
|
| ... An older presentation issue is the tabs versus spaces
| flamewar. At least that one has burnt out. Maybe because
| of the rise of IDE's?
| Zababa wrote:
| > ... An older presentation issue is the tabs versus
| spaces flamewar. At least that one has burnt out. Maybe
| because of the rise of IDE's?
|
| My guess would be not IDEs but autoformatting tools,
| especially when communties like Go all follow one style.
| tsimionescu wrote:
| That would require every bit of software that presents
| this program not only to understand my programming
| langauge, but also to have enough context on the rest of
| the program to actually recognize the function and know
| what each parameter represents.
|
| Not to mention, code is itself a presentation layer. Why
| would you put some presentation concerns in one layer
| (e.g. identifier names, indentation&styling), but others
| in another presentation layer?
| foolfoolz wrote:
| this is true actually. every place we read code needs to
| also parse the code. it's already happening today, that's
| how you get syntax highlighting. the future of all code
| review in the browser is a more similar experience to
| coding in an ide. and eventually the browser will replace
| the ide (and you could say this is already happening too)
| 10x-dev wrote:
| > every bit of software that presents this program not
| only to understand my programming language
|
| You can either change your program to fit existing tools,
| or you can build smarter tools. I prefer the latter.
|
| > code itself is a presentation layer
|
| Not for the tool it isn't
|
| edit: I think we can all agree that ideally we fix this
| in the language itself by adding optional named
| parameters
| tsimionescu wrote:
| No matter how smart the tool is, unless it can see the
| definition of the function, it can't guess the parameter
| name. Code is often shared on mail, chat programs etc,
| requring me to send compilable code snippets to get nice
| presentation out of a blob of text would be significant
| overkill...
| fxtentacle wrote:
| Your IDE could automatically add those annotations as
| rich text or embedded HTML when you copy the source code
| out from the IDE into the E-Mail.
| im3w1l wrote:
| So your solution is to write code that is currently hard
| to read in the hope that someone will make a tool that
| presents it nicely in 10 years?
| fxtentacle wrote:
| Then apparently you need a code review tool which is as
| clever as the IDE.
| Sjonny wrote:
| Code is for humans, not for computers.
|
| Choose: AddElement(object, true, false);
| AddElement(object, true, true); AddElement(object,
| false, false); AddElement(object, false, true);
|
| or AddElement(object, visible::on,
| deletable::off); AddElement(object, visible::on,
| deletable::on); AddElement(object, visible::off,
| deletable::off); AddElement(object, visible::off,
| deletable::on);
|
| The latter is more readable, you can spot bugs easier, you
| don't need to remember which parameter was for visibility,
| and which was for indicating deletable. And it doesn't take
| much more to write this than a confusing boolean. It doesn't
| scale.
| [deleted]
| fxtentacle wrote:
| With a good IDE, the first one can be configured to look
| like the 2nd one.
|
| But the 2nd one will always be more verbose, no matter if
| you need it or not.
|
| So I'd choose the first one.
| Zababa wrote:
| The first one will probably look confusing if you're
| looking at the repo through a web interface though. Or if
| you're looking at examples in a readme. I have personally
| never been limited by my "raw code writing speed", and if
| that was the case, I would look into touch
| typing/autocompletion before sacrificing readability.
| fxtentacle wrote:
| Most source code will also be badly readable if you print
| it out ;)
|
| So should we change our use of C++ to make it more
| GitHub-friendly? Or should we fix GitHub to properly work
| with the C++ source code that already exists?
| jurip wrote:
| That helps when you are writing the code. It's less helpful
| when you are modifying a function being called from all over
| the code base. Arguably smart enough refactoring tools will
| help, but smart enough compilers have been doing wonders for
| decades, too.
| debacle wrote:
| If someone submitted a PR where all of their boolean parameters
| were actually enums I would reject it, open a PR of my own from
| the same branch, and reject that one too.
|
| These clever micro-optimizations are a pathology of bored, well-
| meaning developers.
| nirvdrum wrote:
| Could you please elaborate? To me, it looks like the article
| posits using boolean flags instead of enums is a code
| legibility issue, not a performance matter. There may still be
| good reason to reject a large PR such as the one you described.
| But, I don't get where the micro-optimization appears.
| debacle wrote:
| It's a readability optimization, not a performance
| optimization.
| CRConrad wrote:
| What's wrong with readability optimizations, be they micro-
| or macro-?
| mtreis86 wrote:
| There are a couple ways to approach it in Common Lisp, probably a
| few more than I have here.
|
| One, optional and keyword arguments can have defaults.
| (defun calc-formula (ia ib &optional (gain t)) ...)
|
| Two, you could use a dynamic variable and a closure. Outside the
| body of the let the gain var returns t, within the body of the
| let it returns nil. (defvar *gain-enabled* t)
| (defun calc-with-gain (ia ib) ...) (let
| ((*gain-enabled* nil)) (defun calc-without-gain (ia ib)
| ...))
| cassepipe wrote:
| What about copy pasting functions and make variants? With a hint
| in the name about what the variant does? Copy pasting and
| modifying seems like a safe low effort working solution. Is this
| considered bad practice?
| Jtsummers wrote:
| Yes. It's not a bad first-pass, but it can easily lead to
| problems if there are too many instances of it. In particular,
| if there is _any_ error in the initial program that 's gone
| undetected or any change to the requirements that it
| implements, then you have to fix every single copy. Good luck.
| stared wrote:
| It is a bane of numerical code, in which there are many flags,
| quite a few parameters (with values 0. or 1.). Moreover, some
| flags are conditional (e.g. the value of the argument 5 matters
| only if the flag at position 3 is true).
|
| In a much simpler case of arcs in SVG, I still need to check
| flags to do the correct path (https://developer.mozilla.org/en-
| US/docs/Web/SVG/Tutorial/Pa...).
| Joker_vD wrote:
| In Erlang, one would usually pass atoms like 'with_gain' or
| 'without_gain', and substitute default values with arity-
| overloading: e.g. there would be two calc_formula functions, one
| with 2 arguments and one with 3 arguments, and the 2-argument one
| would simply call the 3-argument with the last parameter set to
| 'with_gain'.
|
| And in case of really large number of parameters, one would
| generally pass either a map or (in older code) a proplist:
| #{is_gain => true, other_param => 42, ...} or [{is_gain, true},
| {other_param, 42}, ...]. There is no beautiful syntax for
| destructuring all that stuff with default values, unfortunately.
| peoplefromibiza wrote:
| > There is no beautiful syntax for destructuring all that stuff
| with default values, unfortunately.
|
| Actually there is one: records with default values.
|
| https://erlang.org/doc/programming_examples/records.html
| jameshart wrote:
| Inform7 - an interactive fiction language - is often instructive
| in suggesting different approaches to syntax from those used in
| mainstream programming, and indeed in this case it has a couple
| of interesting constructions to avoid naked Booleans.
|
| Most directly relevant to this, it has the concept of 'options'
| as arguments to 'phrases' (which are basically functions). This
| would let you write something like: to decide
| what number is the calculated formula for (a: a number) and (b:
| a number), with gain or without gain
|
| And within the function you could use 'with gain' and 'without
| gain' as if they were booleans: If with gain,
| decide on a+b;
|
| And at the calling site you would call the function like so:
| Let c be the calculated formula for 3 and 4, with gain
|
| (http://inform7.com/book/WI_11_14.html)
|
| Obviously in Inform7 you are more likely to be using this in a
| much more naturalistic way, effectively adding 'adverbs' to the
| 'verbs' your phrases are defining: To recount
| the story so far, cursorily or in great detail...
|
| Another similar Inform language feature is its mechanism for
| Boolean properties.
|
| You can declare a Boolean property on a 'kind' or a 'thing' just
| by saying that it 'can' be true: A chair can be
| comfy. The table can be scratched.
|
| You can also use 'either/or' forms to make these booleans richer
| and more expressive: A chair can be comfy or
| hard.
|
| (You can also go on and add more values, of course - at this
| point it's really an enumeration)
|
| These sorts of properties on kinds become 'adjectives', and you
| can use them in both declarations: The
| overstuffed armchair is a comfy chair in the living room.
|
| And in expressions: If the target is a comfy
| chair...
|
| The idea that Boolean parameters are really adverbs and Boolean
| properties are really adjectives I think says something quite
| profound, and there's definitely room for other languages to do
| better at acknowledging the first-class role these kinds of
| constructs could have with the right syntax.
| 3jckd wrote:
| I agree that using booleans like this can be confusing. But, imo,
| it's more confusing to have a bunch of wrapper functions that
| create abstraction madness.
|
| I mostly write computation/math-related code and I find using
| named arguments to be a good practice. This is also quite similar
| to OP's enum approach, e.g. sth like `calc_formula(a, b,
| is_gain=True)`.
|
| To be fair, the older I get, the more I like explicit arguments
| for everything like in Swift (and smalltalk iirc).
| magicalhippo wrote:
| In Delphi I try to use sets over boolean parameters. Then I can
| easily add new possible set members instead of introducing more
| parameters. type FuncParam = (fpDoThis,
| fpDoThat); type FuncParams = set of FuncParam;
| function MyFunc(arg1, arg2: integer; params: FuncParams):
| integer; begin result := 0; if
| (fpDoThis in params) then result := DoThis(arg1);
| ... end; // supply directly
| MyFunc(123, 42, [doThis, doThat]); // or indirectly
| params := []; if (condition) then
| Include(params, doThat); MyFunc(111, 222, params);
|
| Delphi ain't the most elegant language out there, but the set
| functionality is pretty nice.
| CRConrad wrote:
| > Delphi ain't the most elegant language out there
|
| Citation, as they say, needed. :-)
| magicalhippo wrote:
| Any anonymous function will do :P
|
| Not knocking it, after all it's my daily driver. You can do
| quite a lot with it these days and it can be quite
| productive.
| unnouinceput wrote:
| Delphi has overload and default parameters. The problem in
| article is non existent for a good Delphi programmer.
| rocqua wrote:
| In python, I love keyword-only arguments for this. Then the
| caller has to write: v = calc_formula(ia, ib,
| is_gain=true)
|
| You also have the option of defining a default value for the
| argument so the old call-sites don't even need modification.
| BenFrantzDale wrote:
| In C++ I've gotten in the habit of `enum class is_gain : bool {
| no, yes };` so the call site is `v = calc_formula(ia, ib,
| is_gain::yes);`. An advantage is that such stron Boolean-like
| types can be passed repeatedly and maintain their type safety.
| rocqua wrote:
| The 'require named argument' solution is less strong than the
| enum solution. (An enum is also available in python). Indeed
| re-use of the plain bool in Python is less clear, as is
| passing it on. This makes the enum the best solution.
|
| However, enum is also a heavy-duty solution. It requires
| slightly more typing, but more importantly, it requires
| exporting an enum to all call-sites. Both in C++ and in
| python this is not desirable.
|
| I'd say the enum should be in the toolbox, especially if the
| flag is important to the business logic of the code, and is
| likely to thread throughout it. But for quick work, a key-
| word only argument can work just as well. Especially if the
| flag is never to be passed on.
| hermitdev wrote:
| It's worth noting that in Python, the enum will be slower
| than using a bool (how much slower? I don't know - I
| haven't measured it), if for no other reason than the
| repeated name lookups. Is it worth fretting over for
| something that's called occasionally? Probably not. If it's
| something that's going to be called a lot, e.g. in a tight
| loop, then it's something to be concerned about.
| InitialLastName wrote:
| >... it requires exporting an enum to all call-sites. Both
| in C++ and in python this is not desirable.
|
| Given the reasonable namespacing that C++ and python
| [modules] provide, and that you have to export the complete
| calling specification of the function to the caller anyway
| (whether it's an enum, a positional boolean or a keyword
| argument), what's the drawback of the enum option?
| tcheyne wrote:
| A lightweight alternative I often use is to make a named
| constexpr at the callsite. `constexpr bool IS_GAIN = true; v
| = calc_formula(ia, ib, IS_GAIN);`
| gpderetta wrote:
| I also enums as a low syntactic overhead solution for
| strongly typed booleans in C++, although I would do something
| like: enum gain_type { enable_gain,
| disable_gain };
| solarmist wrote:
| I forget which version it starts in, but in newer python's you
| can have keyword only arguments (they can't be positional like
| in your example) def calc_formula(first,
| second, *, is_gain=False): ...
| quda wrote:
| you should document your code instead of over-complicating it.
| flohofwoe wrote:
| As pointed out in some of the article's comments, a much better
| solution would be if all languages allowed putting the name in
| front of function parameters (and while at it, also not enforce a
| specific parameter order and skip default-value parameters).
|
| A workaround in C99 (and more limited in C++20) is to use a
| single struct which bundles all the function parameters, and then
| use designated initialization, this also makes function
| paramaters optional, and at least in C99 they can appear in any
| order: my_func((my_struct_t){
| .a_bool_flag = true, .another_bool_flag = false,
| .a_string = "Hello World" });
|
| This is mainly useful for functions which take many parameters.
| One downside in C99 (but not C++) is that there's no way to
| define default values for struct items (other than zero/false).
| mistahenry wrote:
| Kotlin does this, and I find it's massively improved the
| readability of our codebase
| kevin_thibedeau wrote:
| For C* langs you can just insert a block comment with the arg
| name: my_func(/*a_bool_flag*/ true, yadda);
| city41 wrote:
| The downside to this approach is it often doesn't survive
| refactors. People change the method signature, update call
| sites, and often ignore the comments. Named parameters avoid
| this downside, it's a real shame named parameters are not
| more common in languages.
| emptysea wrote:
| Linters can check for this sort of thing, for example Error
| Prone[0] has a lint[1] for this.
|
| Totally agree this is better to be in the language proper
| so we don't need this extra tooling.
|
| [0]: https://errorprone.info
|
| [1]: https://errorprone.info/bugpattern/ParameterName
| adzm wrote:
| This even works in latest MSVC as well as clang and gcc!
|
| https://pdimov.github.io/blog/2020/09/07/named-parameters-in...
| flohofwoe wrote:
| Yep, in C mode (not C++) this has been working in MSVC
| already since VS2015. Clang also allowed the full C99
| designated intitialization feature set in C++ long before
| C++20 as non-standard language extension.
| amw-zero wrote:
| Many newer languages support named function arguments: Swift,
| Kotlin, etc.
| sbuttgereit wrote:
| Some rather old fashioned languages do as well... for example
| PostgreSQL's PL/pgSQL supports named function arguments, too.
| cphoover wrote:
| In JS we can use JSON/object literal syntax and object
| destructuring to serve a similar purpose:
| myFunc({ aBoolFlag : true,
| anotherBoolFlag : false, aString : "Hello World"
| }); const myFunc = ({ aBoolFlag, anotherBoolFlag,
| aString }) => { /* do something with them... */ };
| kall wrote:
| I started doing this because of React but at this point ({})
| is my default way of starting a function. The only thing I
| dislike is that it's not super ergonomic for explicit
| typescript declarations (but great when using typescript to
| check .js files).
| stnmtn wrote:
| I don't find it too bad to do interface
| IFunctionParameters { userId: string; name:
| string; age: number; } const
| example = ({userId, name, age}: IFunctionParameters) =>
| {...}
| Certhas wrote:
| In Julia you have positional and keyword arguments, separated
| by a semicolon. function f(; a, b) a+b
| end
|
| Default values are optional. This can only be called with named
| arguments like f(; a=5, b=7). Unfortunately the separation
| isn't required to be explicit when calling the function, so
| calling f(a=5, b=7) also works. Generally calling functions is
| extremely permissive (e.g. a function with two positional and
| two keyword arguments function g(a, b=5; c, d=8) can be called
| with keywords and positions interleaved g(1, c = 7, 5)),
| leading to potential confusion at the call site. Our coding
| guidelines enforce separation of positional and keyword at call
| sites, and with that additional restriction I have found Julias
| behaviour in this regard very pleasant. E.g.:
| calc_something(a, b; bool_flag=true)
|
| is the best style for this type of thing that I have seen.
| cormacrelf wrote:
| From the article:
|
| > I'd like a language that can contextually resolve enums, so I
| can just type something like calc_formula( ia, ib, is_gain ).
|
| Swift does this and it should be considered for any new language
| design. Enum.variant can be shortened to .variant, including for
| variants with data in them, .variant(arg). Perfect solution,
| because the dot is an autocomplete trigger.
| henrikeh wrote:
| Fwiw, Ada does this too.
| Camillo wrote:
| Does this feature have a general name? Swift users seem to
| call it "dot syntax", which is not a good name. I want to
| google "C++ should have <this feature>" and find a proposal
| from 2013 that never moved forward, but "C++ should have dot
| syntax" is just silly.
| henrikeh wrote:
| Ada doesn't use the "dot syntax"; instead the enumeration
| literal is written as is without a dot. If I write:
| type Number_System is (Bin, Dec, Oct); type Month
| is (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov,
| Dec); procedure Foo (A: Number_System; B:
| Month);
|
| Then this is a valid call: Foo( Dec, Dec
| )
|
| https://godbolt.org/z/eP5qMj3K1
|
| When the type is explicit, the Ada standard calls this a
| "Qualified expression". But I would just say that it is a
| kind of type inference for enumerations.
|
| https://www.adaic.com/resources/add_content/standards/05aar
| m...
| cormacrelf wrote:
| It's not just for enums in Swift, it's applicable to all
| kinds of static functions, constants, initialisers. Has to
| be static (in the Swift/Java/etc sense) to work. Maybe
| "contextual members" or "contextual statics". I like the
| latter.
| mayoff wrote:
| The official Swift name of the syntax is "implicit member
| expression".
|
| https://docs.swift.org/swift-
| book/ReferenceManual/Expression...
___________________________________________________________________
(page generated 2021-09-20 23:02 UTC)