[HN Gopher] Chaining, or why you should stop returning void (2012)
       ___________________________________________________________________
        
       Chaining, or why you should stop returning void (2012)
        
       Author : debdut
       Score  : 41 points
       Date   : 2021-11-05 12:10 UTC (2 days ago)
        
 (HTM) web link (jamie-wong.com)
 (TXT) w3m dump (jamie-wong.com)
        
       | AdrianB1 wrote:
       | I love this kind of blog posts because not only they present
       | different ways of doing things and a fairly sane discussion about
       | it, but also showing the author learning from experience and
       | telling about it.
       | 
       | Young people are very creative (mostly between ages of 15 and 25)
       | and that creates new ideas. Experienced people test the new ideas
       | and draw conclusions. We need both to progress, the first should
       | be as creative as possible and the later as analytical thinking
       | as possible. It is the innovation pipeline, the first throw the
       | ideas, the others refine, filter and select.
        
       | rchaves wrote:
       | A better reason to not return void is that it will make you think
       | more about immutability and side-effects. Not returning void
       | allow you to reduce the hidden state changes or at least to
       | manage them better
        
       | thriftwy wrote:
       | These tricks ate necessary due to lack of property assignment
       | syntax in languages such as Java. C# does not have this problem
       | and they may start pointing fingers.
        
         | resoluteteeth wrote:
         | There are a bunch of different approaches that other languages
         | that take that make this unnecessary as well.
         | 
         | Some functional languages simply don't tend to use mutable
         | objects with method/property syntax so you can just chain
         | function calls to achieve the same result. (Also some languages
         | have syntactic sugar to do the same thing for method calls)
         | 
         | VB.net (unpopular as it is) has "With" statements that allow
         | you to simply avoid repeating the name of the object you are
         | mutating with method calls/property assignments (this looks
         | very similar to a "fluent api" but it's just a serious of
         | normal property assignments with the object name omitted using
         | a with statement):                 With theCustomer
         | .Name = "Coho Vineyard"           .URL =
         | "http://www.cohovineyard.com/"           .City = "Redmond"
         | End With
        
           | thriftwy wrote:
           | Functional programming won't save you from the fact that you
           | don't want to have constructor with dozen unnamed parameters.
           | That's also fragile in the face of code change.
        
             | Smaug123 wrote:
             | Well, the situation is a bit better than you make out. Your
             | parameters may be unnamed, but they should at least be
             | _typed_. Cases like `Directory.Move : IDirectoryInfo - >
             | IDirectoryInfo -> unit` are a bit of a pain to give
             | unambiguous types, but a twelve-parameter method in which
             | every parameter has a different type (`CustomerId`,
             | `BankId`, whatever - tiny types) is orders of magnitude
             | less fragile than one in which everything is a string.
        
               | thriftwy wrote:
               | It's still not good enough. Java also gives you typing
               | but builders and chained setters are still used.
        
         | thrower123 wrote:
         | Fluent builder patterns are wildly popular in C# however.
         | 
         | At this point it seems like a stylistic choice.
        
       | jedimastert wrote:
       | First, needs a (2012).
       | 
       | Second, this is the very top:
       | 
       | > Edit from 2018: The tone of this post is a little cringe-worthy
       | to me now in retrospect, and I think that many APIs are probably
       | clearer when you return void. Leaving this here for posterity.
        
         | amsully wrote:
         | It's amazing how easily my brain skips a subtitle like this.
         | Years of subconsciously parsing advertisements has won over my
         | elementary school teachers encouragement to "read the whole
         | passage."
        
           | spicybright wrote:
           | Same. Bites me more than I'd like to admit.
        
           | Shorn wrote:
           | Y'know, I've been wondering lately what's been going on with
           | my reading comprehension. I just wrote off all these errors
           | over the last few years as part of getting old.
           | 
           | I've been so busy patting myself on the back for succeeding
           | in not giving any attention to ads - I never stopped to think
           | there might be a cost.
        
       | avl999 wrote:
       | Chaining works well in builder objects and perhaps in some GUI
       | programming or some other very specialized cases but most of the
       | classes I write don't have sufficient mutable state to make this
       | kind of thing worth it.
       | 
       | My advice would be to almost always start with a void (unless
       | builder objects) and then add chaining if the evolution of your
       | API dictates that chaining together calls would be a common
       | enough usecase.
        
       | alerighi wrote:
       | This can be inefficient depending from the language and the
       | compiler. The compiler many not always know that the this that
       | you return is the same of the object you are calling the method
       | to, and that can prevent further optimizations.
       | 
       | I don't see either any readability advantages of doing so, doing
       | that just for not typing the name of the variable is nonsense,
       | also the code is less clear to the reader (you have to know that
       | the method that you call returns the same objects, it's not
       | obvious, it can as well return another object, or a copy of that
       | object). It's more explicit doing it the normal way.
        
       | kevinmgranger wrote:
       | There's competing incentives for API ergonimics and making
       | mutations / ownership explicit. Kotlin's scope functions really
       | get you the best of both worlds with this.
       | 
       | Lambdas can have a "receiver", which acts as the (implicit or
       | explicit) `this` for the lambda.
       | 
       | `apply` even returns the object itself, so the first example
       | could be:                   frame.add(FormPanel().apply {
       | setSize(300, 150)             add(usernameLabel)
       | add(usernameField)             add(passwordLabel)
       | add(passwordField)         })
        
       | ris wrote:
       | You should categorically not be implementing chaining semantics
       | unless your operations are non-mutating.
       | 
       | If they are mutating and you return the same object, it's
       | possible for people to go _years_ without realizing that 's
       | what's happening (no, most people don't read the manual),
       | assigning the result to something else, reusing the original for
       | another thing and introducing subtle bugs all over the place.
       | 
       | See the design of python's list `.sort()` returning None for this
       | exact reason.
       | 
       | (and of course there are sometimes performance penalties to
       | implementing non-mutating methods, so there are lots of valid
       | cases for not implementing chaining semantics)
        
         | hcrisp wrote:
         | Or at least adopt a semantic preference that informs the user
         | as to what is happening, e.g. Python has .sorted() for for non-
         | mutating method vs .sort() for mutating method.
        
           | mgraczyk wrote:
           | `sorted()` is a builtin function, not a method. It can still
           | be confusing, but it's more clear IMO that `sorted(array)`
           | will not mutate vs a hypothetical `array.sorted()`.
        
         | eyelidlessness wrote:
         | This is why I hate JS Array#sort. Its return type being an
         | array gives the impression it's not sorting the original array,
         | leading to a lot of hard to track down bugs.
         | 
         | It's also one of the biggest reasons I discourage use of
         | lodash. Sure, _most_ of its methods are pure and return new
         | values. But some do mutate the input, and they're
         | interchainable (sorry, couldn't resist). The only way to know
         | which you're using is to memorize or constantly consult their
         | _enormous_ API.
        
         | theteapot wrote:
         | > You should categorically not be implementing chaining
         | semantics unless your operations are non-mutating.
         | 
         | What if it's a method of the data object?
        
           | Brian_K_White wrote:
           | data should not have methods, but that's a whole other
           | religeous war :)
        
         | AtlasBarfed wrote:
         | Builder pattern for complex initialization like db connections
        
           | timando wrote:
           | Return a new builder that has the changes applied.
        
         | gpderetta wrote:
         | In c++ you can make your mutating chaining operations rvalue
         | qualified, have them return by value and move _this_ into the
         | result. So you have purely functional operations that still don
         | 't create copies in practice.
        
       | emerongi wrote:
       | I like function chaining more. E.g. Elixir's `Map.set(map, field,
       | value)` function combined with the `|>` operator, which feeds the
       | value from the previous operation in to the function as the first
       | argument:                   person_map         |> Map.set(:name,
       | "Peter")         |> Map.set(:age, 37)         |>
       | Map.set(:married, true)
       | 
       | It's so much fun to look at a chain of generic functions working
       | on a generic data structure, while keeping the code clear. Makes
       | me feel all warm inside. Immutability and functional programming,
       | ain't it nice, huh?
        
         | aaaaaaaaaaab wrote:
         | >It's so much fun to look at a chain of generic functions
         | working on a generic data structure, while keeping the code
         | clear. Makes me feel all warm inside.
         | 
         | Yeah, that's usually a bad sign. Programmers who think some
         | language features are "fun" usually end up overapplying them
         | and making life harder for everyone who just want to do their
         | job and ship stuff.
         | 
         | Programming languages should be simple, boring and self-
         | evident.
         | 
         | Keep "fun" in your hobby projects.
        
           | pasquinelli wrote:
           | i'm confused, are you responding to their example or to their
           | vibe?
        
           | emerongi wrote:
           | You do you. Software development is a large enough field, our
           | paths don't have to cross.
        
           | macintux wrote:
           | Elixir (really, Erlang) has tightly-constrained semantics
           | around immutability and exception handling that make this
           | feature a natural application of the core concepts.
        
       | a_lost_needle wrote:
       | My functional API's never return void because they're functions
       | and immutability is the benefit there. Returning void means
       | you're either mutating something or interacting with something
       | thing that does. Also, chaining can be dangerous, as if it's not
       | implemented correctly, can hide errors, or not have adequate
       | failure tolerance, and cleanup can be a mess if a stream or
       | connection disconnects in the middle of your chain.
       | 
       | But in an OO project, void is a really useful. If you're doing
       | game programming, mutability is required for all but the most
       | trivial games. And throwing in more functions and more variables
       | on the stack isn't going to benefit anything. You click a button,
       | the gun fires. I don't need to check the return, it add no value,
       | adds complexity and isn't idiomatic to the language.
       | 
       | Use the right tool for the job.
        
       | laurent123456 wrote:
       | Chaining is fine when building up an object, but definitely not
       | something that should be systematically used. That's making the
       | interface more complicated for little gain.
        
         | G3rn0ti wrote:
         | Why? Whenever you return ,,void" you return ,,this". That does
         | not make anything more complicated. It is a pattern that suits
         | itself well in front end code. When it comes to async. code,
         | however, it's a different story since you return promises in
         | this case that are also chainable.
        
           | laurent123456 wrote:
           | > When it comes to async. code, however, it's a different
           | story since you return promises in this case that are also
           | chainable.
           | 
           | So that's one issue. That's what I mean by the interface
           | being more complicated - void is void, but if you return
           | something there might be some edge cases, some of which are
           | difficult to predict. Personally I think it's not worth the
           | trouble.
        
       | aaaaaaaaaaab wrote:
       | Yeah, how about no?
       | 
       | Mutating methods should return void, period.
        
       | mjgoeke wrote:
       | I work in C# but have come to a similar conclusion to his note at
       | the top of his article.
       | 
       | C# supports extension methods, so if you want you can augment the
       | original 3rd party class with your own (chaining in this case)
       | methods. Or author your own "fluent interface" on your own
       | classes, as he's done here.
       | 
       | I'm the end, most of the time I found I was wanting a function
       | 'forward composition' operator (like F#'s |> ). And the only
       | place the chaining methods really made sense was factory
       | classes/methods for complex declarative configuration.
       | 
       | The only ones that stuck for us were ORM mapping declarations,
       | and factories for setting up complex domain specific data setup
       | in tests.
        
         | bob1029 wrote:
         | For C#, the only method chaining we really do today would be
         | for LINQ (very heavy usage) and AspNetCore startup logic.
         | 
         | If you are super addicted to certain language sugar like object
         | initializers, then wrapping some of your business logic (e.g.
         | mappers) with extension methods is not a terrible path. E.g.:
         | public Thing MyFavoriteThing => new Thing       {         Id =
         | Guid.NewGuid(),         TheThing =
         | _someLocalProperty.SomeTransform().Trim(),
         | MyOtherInitalizedFact = true       };
         | 
         | The alternative would look something like:
         | public Thing MyFavoriteThing()       {         var result = new
         | Thing         {           Id = Guid.NewGuid(),
         | TheThing = SomeTransform(_someLocalProperty),
         | MyOtherInitializedFact = true         };
         | result.TheThing = Trim(theThing);         return result;
         | }
        
       | MichaelBurge wrote:
       | You could also make a single-letter alias:
       | auto& r = rectangle;         r.width(100);         r.height(100);
       | 
       | I like to use very short variable names(single or 3 letters at
       | most) but fully write out the type:                   Rectangle
       | r;
       | 
       | Which is similar in spirit to aliasing the variable.
       | 
       | "Rectangle" is probably not a good example, because rectangles
       | are pure data used for many different purposes. So "rectangle" is
       | often a poor variable name. But many programs use 1 type for 1
       | purpose, and then the type alone is enough.
        
       | BoardsOfCanada wrote:
       | I like dart's cascade operator (..) a lot, that way you can chain
       | calls on an object without the functions returning this. I'm sure
       | other languages have something similar, kotlin has apply which
       | feels a bit more heavy handed though.
        
         | [deleted]
        
         | mpweiher wrote:
         | Pretty sure this was taken from Smalltalk, which uses the
         | semicolon as the cascade.
         | 
         | So                  a b c d.
         | 
         | sends c to the result of sending b to a, and d to the the
         | previous result etc.                  a b; c; d.
         | 
         | Sends b, c, and d to a. This is often indicated visually:
         | a b;          c;          d.
        
         | Someone wrote:
         | Pascal (https://www.freepascal.org/docs-html/ref/refsu62.html)
         | and Visual Basic (https://docs.microsoft.com/en-
         | us/dotnet/visual-basic/languag...) have _with_.
         | 
         | That serves a similar purpose, (in VB only for accessing
         | multiple fields in an object, I think)
         | 
         | Visual Basic's syntax is a bit safer. Requiring that seemingly
         | superfluous dot means there can't be confusion between
         | referencing fields and referencing local or global variables.
         | 
         | In Pascal adding a field to a record whose name shadows that of
         | a local can change the meaning of the statements in a _with_
         | statement, and removing it may silently 'fix' a broken
         | reference to a no longer existing field.
         | 
         |  _with A, B, C, D do_ is asking for trouble even more.
        
         | donatj wrote:
         | Oooh I didn't know this was a thing. I had that as an idea
         | recently but it's really interesting to hear it already exists!
         | 
         | I have been hearing a bunch of interesting things about Dart
         | lately and feel like I should check it out.
        
         | ptx wrote:
         | What's heavy-handed about Kotlin's apply? It's just an
         | extension function in the standard library (so not a separate
         | language feature, if that's what you mean by "heavy"?) and
         | solves the problem cleanly IMHO.
         | 
         | You can easily implement a similar extension function in C#,
         | although the syntax is slightly uglier and you don't get the
         | implicit receiver in the lambda.
        
           | BoardsOfCanada wrote:
           | I just mean it looks a bit more obtrusive to me than just ..
           | 
           | I agree that it speaks volumes about the power of the
           | language itself.
        
       | [deleted]
        
       | 74B5 wrote:
       | It's the fluent interface pattern.
       | 
       | https://en.wikipedia.org/wiki/Fluent_interface
        
         | baktubi wrote:
         | Yeah some of the issues with error handling are pretty serious.
         | I would say in C++ it could be useful only for simple "nothrow"
         | routines for basic object configuration; this would apply for
         | cases when the configurations should happen after construction.
         | 
         | Aside from that I wouldn't use it for complex algorithms.
         | 
         | It's almost like a weird intersection between functional and
         | object oriented paradigms.
         | 
         | But there's not much benefit from these two approaches:
         | 
         | rect.setHeight(125).setWidth(250);
         | 
         | rect.setHeight(125); rect.setWidth(250);
         | 
         | That being said, for algorithms similar in spirit to the
         | chaining I think the C# Linq language is a good example of
         | something that works well in a "chained" fashion.
        
         | salawat wrote:
         | God, I hate that pattern.
         | 
         | Give me an API that takes a key/value pair where key is the
         | name of the field to be set, and value is the value to be set.
         | 
         | Easy to read. Great way to get people to pay attention to tge
         | datamodel, Low overall maintenance burden.
         | 
         | As opposed to: lets add another bloody withMethod() to the API,
         | 
         | so people can write
         | 
         | MethodsLikeThis .withThis(Thing) .andThat(Stuff)
         | .atTheEndofWhich(aThingwithAnEnd) .someone(you) .hopes(new
         | Hope("There is a point to it all)).but().alas("There is not.");
         | 
         | So now my stack traces look daft when things break in a
         | contrived attempt to make things prettier to read when
         | otherwise it could have been:
         | 
         | Unable to set value for key:"Kye" value:"if you spell it right,
         | it will set it. Unless it isn't even the right datatype".
         | Unknown field:"Kye"
         | 
         | Build your state packet. Prime your logical machine. Push the
         | button. Verify output. Do not try to turn my test suite into a
         | work of effing Shakespeare.
         | 
         | Maybe that's just me being crotchety though.
        
           | Smaug123 wrote:
           | > Give me an API that takes a key/value pair where key is the
           | name of the field to be set, and value is the value to be
           | set.
           | 
           | Not in a statically typed language, please! It _should_ be a
           | compile-time error to try and set the key `Kye`.
        
       | flohofwoe wrote:
       | The "Galaxy Brain" version of this advice is using plain old
       | designated initialization of 'option bag' data structures. Here's
       | a non-trivial C99 example from the sokol_gfx.h API to create a
       | pipeline-state-object for 3D rendering:
       | state.pip = sg_make_pipeline(&(sg_pipeline_desc){
       | .layout = {                 .attrs = {
       | [ATTR_vs_position].format = SG_VERTEXFORMAT_FLOAT3,
       | [ATTR_vs_color0].format   = SG_VERTEXFORMAT_FLOAT4
       | }             },             .shader = shd,
       | .index_type = SG_INDEXTYPE_UINT16,             .cull_mode =
       | SG_CULLMODE_BACK,             .depth = {
       | .write_enabled = true,                 .compare =
       | SG_COMPAREFUNC_LESS_EQUAL,             },             .label =
       | "cube-pipeline"         });
       | 
       | Chaining might be useful for data processing pipelines, but not
       | for simple data initialization tasks IMHO.
        
         | jimbob45 wrote:
         | I know C is an extremely deep language but that doesn't look
         | like C99 to me. Since when can you set struct member variables
         | piecewise in a...function definition?
         | 
         | Is this maybe a custom compiler? Or is this actually C++?
        
           | nwellnhof wrote:
           | What you're seeing is a compound literal with designated
           | initializers. Both features were introduced in C99.
        
           | rackjack wrote:
           | It is C99, which surprised me too.
           | 
           | https://stackoverflow.com/a/7487949/11736447
        
           | chisquared wrote:
           | I encountered code like this in a C99 project recently. I
           | still don't understand it, but I believe it's supposed to be
           | possible.
        
           | kevin_thibedeau wrote:
           | It's a C99 compound literal with designated initializer. C++
           | doesn't support it.
        
           | MayeulC wrote:
           | As other commenters have pointed out, those are designated
           | initializers[1], one of my favorite C99 feature.
           | 
           | I's also been picked up recently for inclusion into C++20[2].
           | 
           | However, it seems that C++ adds a specific limitation:
           | 
           | > _all designators used in the expression must appear in the
           | same order as the data members of T_
           | 
           | > _out-of-order designated initialization, nested designated
           | initialization, mixing of designated initializers and regular
           | initializers, and designated initialization of arrays are all
           | supported in the C programming language, but are not allowed
           | in C++_
           | 
           | Which limits the usefulness, as I've used this syntax to let
           | myself reorder struct fields (and add some) without breaking
           | the API (while obviously breaking the ABI). It's a great tool
           | to use while your API is still in flux.
           | 
           | At least an error will be produced, which is much better than
           | just providing values and hoping that the API/ABI remains
           | stable.
           | 
           | [1]: https://en.cppreference.com/w/c/language/struct_initiali
           | zati...
           | 
           | [2]: https://en.cppreference.com/w/cpp/language/aggregate_ini
           | tial...
        
         | matheusmoreira wrote:
         | Designated initializers is the most amazing feature of the C
         | language. I don't understand why no other language ever copied
         | it...
        
           | jmercouris wrote:
           | It exists in Lisp, even before C.
        
           | AtlasBarfed wrote:
           | Groovy has this ability I think if I can parse that code. My
           | C code experience predates C99
        
       ___________________________________________________________________
       (page generated 2021-11-07 23:02 UTC)