Don't return yourself

TL;DR: Your caller already has your reference, he does not need another one.


It is semi-unrare practice to use return this when it is desired to create chainable method calls.

The practice is often called "method chaining" or "fluent interface". (Note: sometimes those are not synonyms)

A class using this practice might look like this: (I'll be using JavaScript here, sorry.)

class Animal {}
class StripedAnimal extends Animal {
	color = "white";
	name = "";
	vore = null;
	paint(c) {
		this.color = c;
		return this; // what?
	}
	baptize(n) {
		this.name = n;
		return this; // ew
	}
	feed(f) {
		if (f instanceof Animal) {
			this.vore = "carni";
		} else {
			this.vore = "herbi";
		}
		return this; // 🤢
	}
	self() {
		return this; // 🤮
	}
	reflect() {
		if (this.color === "orange" && this.vore === "carni") {
			return "Am tiger.";
		}
		if (this.color === "white" && this.vore === "herbi") {
			return "Am zebra."
		}
		if (this.color === "black") {
			throw Error("no discernible stripes");
		}
		return "Am striped unicorn.";
	}
}

This class can then be used like this:

reflection = new StripedAnimal().paint("orange").baptize("Richard").feed("leaf").self().self().reflect();

or

reflection = new StripedAnimal()
	.paint("orange")
	.baptize("Richard")
	.feed("leaf")
	.self()
	.self()
	.reflect();

If the class didn't have fluent interface, but more traditional method implementations, you would have to call it like so:

stripedAnimal = new StripedAnimal();
stripedAnimal.paint("orange");
stripedAnimal.baptize("Richard");
stripedAnimal.feed("leaf");
stripedAnimal.self();
stripedAnimal.self();
reflection = stripedAnimal.reflect();

People say that this looks very verbose compared to the fluent interface, hence fluent interface is better.

But there are multiple ways to solve the verbosity.

s = new StripedAnimal();
s.paint("orange");
s.baptize("Richard");
s.feed("leaf");
s.self();
s.self();
reflection = s.reflect();

Tradeoff

Ugly implementation vs. ugly usage.

Some people will insist that fluent interface is more readable. I don't agree, but I'll entertain the thought for this.

I think we can agree that fluent interface methods are (slightly) less readable:

paint(c) {
	this.color = c;
	return this;
}

Than traditional methods:

paint(c) {
	this.color = c;
}

Or you can choose to not implement accessors at all which is even more readable:

So there is a tradeoff:

If the thing is used once or twice, the added readability of the usage is probably not worth the reduced readability of the implementation.

If the thing is used a lot, the added readability of the usage is probably worth the reduced readability of the implementation.

Should we now use fluent interface when things are used a lot and traditional methods if they aren't?

I think it is simpler not to use fluent interface. Its benefits aren't worth it.

Opposition

Okay, it's subjective.

I agree that it is largely a matter of taste, but taste is relevant to clarity.

DSL

If you want a DSL, make a DSL, don't misuse this L.

C#

Disavow.

The real problem

Can/Should I do:

s = s.paint("orange");

or is it equivalent to

s.paint("orange");

Some methods require the former.

t = s.clone();

This is not fluent interface. The method inherently returns a kindred object. It is not optional convenience.

It is not clear that these methods have different semantics when combining them with fluent interface methods:

reflection = new StripedAnimal()
	.paint("orange")
	.baptize("Richard")
	.clone()
	.feed("leaf")
	.reflect();

When code is idiomatic, readers can understand more of the whole system when reading part of it.

Fluent interface is not idiomatic, generally.

You can argue it's prettier.

You can argue it's useful.

But I don't think you can argue that it is the obvious solution to implementing a method.

There should be one-- and preferably only one --obvious way to do it.

When is it okay to return yourself?

Basically, when you have to.

Examples are Go's append "method" or any other functionality related to C's realloc. They return themselves or a moved and resized version of themselves.

(Yes, it's a function, not a method, but it might as well be a method of the slice type.)

If fluent interface is not used, the peculiarities of methods stand out more, which is a good thing.

Immutable (copy-on-write) fluent interfaces are completely fine, great even. It is necessary for them to return a kindred object or value.

Related