[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)