I keep tripping over "true, false, true"

(allthingssmitty.com)

23 points | by AllThingsSmitty 11 hours ago ago

60 comments

  • dematz 10 hours ago ago

    First, sentences like "Not because it’s complicated. Just because I have no idea what I’m looking at." and "Tiny interruption. Still annoying every time." fatigue me, it's like you have an editor who, no matter what the content is, tries to spice up your writing with lots of little punchy exclamations, not everything needs such emphasis

    Second, this may differ a bit from language to language, but maybe those booleans should not be a boolean: https://gleam.run/documentation/conventions-patterns-and-ant... for example isAdmin boolean could instead be a UserRole custom type, with variants Normal and Admin, which is easier to understand in the function call, and extendable with another Moderator (or whatever) variant

    • megiddo 10 hours ago ago

      Exactly my thought. I know languages differ in support ... but enum is right there.

  • NooneAtAll3 10 hours ago ago

    > toggleMenu(true); That’s clear enough. the meaning is obvious

    so... it does toggle the menu? and toggleMenu(false) doesn't toggle it and keeps it as it is?

    or is it toggle extended menu vs toggle basic menu?

    • ahartmetz 8 hours ago ago

      setMenuVisible()

      If it is unclear what your one-argument function does, the name is the problem and you messed up, not the language. For multi-argument functions, you can have either enums or named arguments or, if you really have to, some kind of builder pattern thing: eatCheese(cheese().fromGoatMilk().withVanillaFlavor()).

    • 9 hours ago ago
      [deleted]
  • PaulKeeble 11 hours ago ago

    There is something to be said for the bitmasks that are so common in C, createUser(user, ADMIN | SENDMAIL); has a lot more clarity than createUser(user, true, false, true);

    I don't mind the object approach used here but its quite verbose in comparison even in Javascript. Having to name the variable and set whether its true or false is a lot more than needs to be done. Booleans in general have quite poor readibility and maintenance especially if a third possibility arrives.

  • sph 10 hours ago ago

    AI;dr: keyword arguments would be great in all languages, not just Smalltalk

    Also, obviously bot/bought account.

    • saghm 9 hours ago ago

      Swift also has them. It's probably the one thing I sometimes wish Rust would copy from them.

      • NekkoDroid 2 hours ago ago

        While I wish every language had them in a way, they do tend to enshrine the argument names in the ABI, so now you can't change those in public APIs. (Main reason I think it shouldn't be part of the ABI is because I think only the necessary things to identify and correctly use a contract should be part of the ABI)

        I have been thinking off if there is a way to have it work without enshrining them in the ABI and my only real idea is: allow arbitrary names at the call site (e.g. `my_function(some_var=1)` or `my_function(some_var: 1)`) but don't enforce the naming, just have linting spit out a warning for it if it doesn't match.

        • saghm 11 minutes ago ago

          I don't feel like anyone finds it onerous to not be able to rename functions as a non-breaking change, so it's not obvious to me that this is much of a deal-breaker. If anything, I'd be thrilled for it to force people to spend more time carefully picking the names of their parameters in public APIs so that the autocompletion/documentation popup in my editor has higher quality information!

      • saghm 3 hours ago ago

        Thinking more about this, I'm realizing that Swift was probably transitively inspired by Smalltalk; although I don't know much about Objective-C, my vague understanding is that it's a bit more inspired by Smalltalk's view of object-oriented programming via message passing than what commonly is considered OO nowadays (which is reflected somewhat in that it doesn't use the typical dot-operator for method calls), and I'm guessing that it was included in Swift as one of the things that was liked about Objective-C (and maybe a little to make interop more direct).

  • kevsim 10 hours ago ago

    This is commonly referred to as "the boolean trap". You'll find lots of articles about it.

  • tantalor 10 hours ago ago

    This one is a classic,

    Avoid the Long Parameter List

    https://testing.googleblog.com/2024/05/avoid-long-parameter-...

  • bradrn 11 hours ago ago

    Also known as ‘boolean blindness‘: e.g. https://cs-syd.eu/posts/2016-07-24-overcoming-boolean-blindn...

  • jchw 9 hours ago ago

    I don't remember where it was originally written, but Qt used to have a fantastic Qt Quarterly about API design that included "The Boolean Parameter Trap", which advised several potential solutions to this very problem. Of course, it was somewhat specific to C++ in what it recommended, but I find that many insights in Qt's API design are more broadly applicable than they may first appear. In this case one insight that stuck with me: it's often better to use an enumeration of two values over a boolean.

    And while searching for a reference, I found the original Qt Quarterly, archived here: https://doc.qt.io/archives/qq/qq13-apis.html - I am sure some of it is just hopelessly outdated, but the general insights probably still hold up. There's a probably-more-modern successor here on their wiki: https://wiki.qt.io/API_Design_Principles

  • tyleo 11 hours ago ago

    In the last couple of years I’ve started using named parameters a bunch more across languages. I consider objects like this close to the JS version of a named parameter. I probably would have thrown “name” in myself so it’s one arg for the whole func.

    I feel like a goal with good code is localizing understanding even if it occasionally duplicates something like a parameter name.

  • j_w 5 hours ago ago

    You should just use objects/structs for function arguments when the arguments have any expectations of being expanded in the future. Use sane default behavior when new arguments/options are added to the function. Consumers of your interfaces will then have a much easier time handling updates down the line.

  • Demiurge 9 hours ago ago

    Named arguments are a great feature in Python. I often forget TypeScript doesn't have this, but I use the object form all the times. As a bonus, you can also declare these arguments in an object an interface type, aptly named.

    • ASalazarMX 5 hours ago ago

      Amen. Was about to comment:

      create_user(user, is_admin=True, send_welcome_email=False);

      Not even article-worthy.

  • edwcross 10 hours ago ago

    OCaml has had labeled arguments for decades, so I assumed other languages would have added something similar by now. In C-style, it would be like:

      createUser(user, ~isAdmin:true, ~sendWelcomeEmail:false)
    
    Even though in OCaml's functional style it is actually like this:

      createUser user ~isAdmin:true ~sendWelcomeEmail:false
    
    Using the fact that a variable named exactly like a labeled argument is automatically assigned to it, we can make the call more concise (especially if reusing existing variables):

      let isAdmin = true in
      let sendWelcomeEmail = false in
      createUser user ~isAdmin ~sendWelcomeEmail
    • Dagonfly 10 hours ago ago

      It's the one thing I miss from Swift when I'm using literally any other language. Interal and external parameter names. I would love for Rust to adopt:

        fn foo(namedParam internalName: bool) { // use internalName here }
      
        fn foo(unnamedParam: bool)
    • jdiff 9 hours ago ago

      This is one of the things I've loved about Gleam, one local variable name and potentially an external label that callers can use to annotate themselves. For example, a function declaration may look like this:

        pub fn pad_end(
          string: String,
          to desired_length: Int,
          with pad_string: String,
        ) -> String
      
      And a call to this function may look like:

        pad_end("123", to: 5, with: ".")
    • eithed 9 hours ago ago

      Think the issue is not with named parameters per se, but with mixing domain logic = there are two different user creation flows, that should be doing two different things (or mostly different things), but are guarded with boolean flag.

      As author points out: "So I’ll usually just make it explicit:

      createAdminUser(user);

      createRegularUser(user);

      Now there’s not much left to interpret. To be fair, this isn’t always bad. Sometimes this is completely fine:

      toggleMenu(true);

      That’s clear enough. This tends to work when:

      - the meaning is obvious

      - the function is small and local

      - there’s only one flag "

    • MathMonkeyMan 10 hours ago ago

      You can do this as a convention in javascript since 2015, but I haven't seen a library that does it:

          > function foo({a, b, c}) {
          ... return {x: a, y: b, z: c};
          ... }
          undefined
          > foo({a: 1, b: 2, c: 3})
          { x: 1, y: 2, z: 3 }
          > const a = 'A', b = 'B', c = 'C';
          undefined
          > foo({a, b, c})
          { x: 'A', y: 'B', z: 'C' }
          >
    • lostmsu 10 hours ago ago

      C# has that

  • ajdude 7 hours ago ago

    This is why I continue to advocate for using Ada. It's not just about memory safety but conscious effort was put into readability.

    Calling the procedure in the article in ada would simply be:

       createUser (user, isAdmin => True, sendWelcomeEmail => False, skipValidation => True);
    • andOlga 6 hours ago ago

      Named arguments are supported very widely. How is this an argument specifically in favor of Ada?

  • derefr 7 hours ago ago

    In languages without compile-time keyword arguments but with block comments, you can use them inline for this.

       createUser(user, /* isAdmin */ true, /* sendWelcomeEmail */ false);
  • Dwedit 9 hours ago ago

    This is where something like "const bool isAdmin = true, sendWelcomeEmail = false" helps. Now your literal values aren't in the function call arguments anymore, but instead their meaning is, you just need to look elsewhere (probably the line right above it) to find their values.

  • frizlab 4 hours ago ago

    This is one of the numerous advantages of Swift: arguments are labeled.

  • tanseydavid 6 hours ago ago

    This very matter is covered nicely in Code Complete.

  • trgn 11 hours ago ago

    named arguments are hacking object literals to provide additional readability. it's ok, but not for all code paths, they have a true overhead. problem is that these things start to become idee fixes in teams (all funcs should have named args!). ideally, this could be fixed in the language.

    • tyleo 11 hours ago ago

      Agree that it would be nice to fix in the language. It seems like something that even a transpiler could take care of.

      Ultimately I think I’d bias towards readability vs the marginal perf increase though.

  • 10 hours ago ago
    [deleted]
  • disillusionist 10 hours ago ago

    I've been using this pattern for the past couple years for the benefits the author mentions. In addition to that, it can help with overly complicated functions (which, ok, could probably be refactored) that have multiple optional arguments.

  • heyitsdaad 10 hours ago ago

    You answered your own question. Call with

    const isAdmin = true; . . . createUser(user, isAdmin, sendWelcomeEmail)

    • rcxdude 10 hours ago ago

      If you swap the order of isAdmin and sendWelcomeEmail you'll get no error from the compiler but now the names will be masking the actual behaviour.

      • arethuza 9 hours ago ago

        Can you imagine how much fun it would be to find that bug?

  • mercury4063 8 hours ago ago

    so this article appears to be a story about a person who realizes their code was bad and also doesn’t know how to reverse lookup a definition? is that what this is? this is a genuine question cause that’s what this seems to reduce to

  • bcjdjsndon 10 hours ago ago

    Isn't this more an issue with typescript? Doesn't your ide give you the declaration if you hover over the call?

    > And I’ve seen real calls like this in production code: > updateSettings(user, true, false, true, false)

    Really? He wants named parameters on all function calls cos he's got a memory like a sieve? This is a long solved problem to me

    • 9 hours ago ago
      [deleted]
  • Jtarii 8 hours ago ago

    This needed an entire article?

    • ASalazarMX 5 hours ago ago

      Only because the language that's used doesn't support named/labeled arguments.

  • paulddraper 9 hours ago ago

    This article relates to two classic principles:

    1. Avoid long parameter lists [1]

    2. Avoid boolean arguments [2]

    For #1, long parameter lists should be named (named arguments, options object, etc).

    For #2, and booleans should be replaced by meaningful enumerations.

    > toggleMenu(true); That’s clear enough. the meaning is obvious

    Actually, it's incredible ambiguous. Is that toggling the menu? Or setting it to the open state? Or something else? I have no idea.

      setMenuState(MenuState.OPEN)
      setMenuState(MenuState.CLOSED)
    
    [1] https://testing.googleblog.com/2024/05/avoid-long-parameter-...

    [2] https://alexkondov.com/should-you-pass-boolean-to-functions/

    • benj111 7 hours ago ago

      I was thinking the same.

      ToggleMenu(); should do it. Or is there something I'm missing? Ie is the function misnamed? OpenMenu(true); maybe what the function is doing?

  • uberman 11 hours ago ago

    Perhaps, jsdocs might help here.

  • esafak 10 hours ago ago

    Usable languages let you use named arguments foo(bar=zaz), and linters let you enforce their use for booleans.

    https://kotlinlang.org/docs/functions.html#named-arguments

  • vrighter 9 hours ago ago

    i like to create enums instead of boolean.

    CreateUser(CreateAdmin::enable);

  • readthenotes1 10 hours ago ago

    Someone want to recreate Smalltalk...

  • rideontime 10 hours ago ago

    I was nodding along with the piece in the first half, then it repeated the same point five more times and I started to smell slop.

    • empath75 10 hours ago ago

      You can tell it was written by claude after just a few sentences, really.

      • chickenimprint 9 hours ago ago

        Even the premise is ridiculous, inlay hints for parameter names have existed for a really long time. What human using a code editor could come up with this "problem"? The comments all seem to be nodding along and even suggesting absolutely hare-brained code like "const isThing = true; ...". I feel like I'm losing my mind. How many interactions on this site are sill organic? Am I talking to a bot right now?

        • tom_ 9 hours ago ago

          Bleep bloop. This has always bothered me for one. Bools for unnamed parameters just always end up a bit of a pain.

          1. Somewhat often, parameter hints just stop working. Why? Who knows! There never seems to be any way to debug these mechanisms. They just stop working and you're forced to do stuff like delete random cache folders or toggle random options, as suggested by randos from Stack Overflow posts in 2017, until it maybe starts working again (for now)

          2. Parameter hints sometimes aren't available when you need them. Xcode, for example, only seems to be able to put them inline, as text in the document that you replace with the arguments, something available only when writing code. You can't get them to pop up on demand as a reader as you can with, say, Visual Studio

          3. You might be examining the code in some kind of review UI, rather than a code editor, in which case you're going to have to do some back and forth. You might be right in the middle of something yourself, and not in a position to retrieve the code locally to examine it in the usual editing environment. (I don't think you should go overboard optimising for this case, but I think it worth considering)

          4. From the type checker perspective, all bools are equivalent. If you pass "false" for one bool parameter, then you could just as well pass that same "false" for another one - complicating rearranging or adjusting parameters, because, depending on the type of change you make, you may not get a good set of diagnostics to work through. And it gets worse if you've got defaulted or optional parameters

        • gcmeplz 9 hours ago ago

          yeah, I immediately came to the comment section to look for inlay hints, and your comment is the only one that mentions them. I guess many people might not be aware that they're a standard editor option?

          fwiw, while inlay hints are great, they don't work in either git-delta or github, so they're not availabe in a good chunk of the places that I'm looking at code, so for TypeScript, I do lean towards object arguments with keys the way the article suggests.

      • Jtarii 8 hours ago ago

        The comically unncessary section headers are also incredibly LLM coded.

  • chickenimprint 10 hours ago ago

    I wonder whether a person prompted this slop and is somehow unaware of the existence of LSPs, or if it's entirely automated and the planning subagent hallucinated this being an issue for humans.

  • gifflar 5 hours ago ago

    [dead]