On Sun, Jan 19, 2020 at 04:27:30PM -0500, David Mertz wrote:
I'm not sure what you intend here:
A fluent interface is normally implemented by using method chaining
Indeed. *Implemented by*, not "the same as".
https://en.wikipedia.org/wiki/Method_chaining to implement method cascading https://en.wikipedia.org/wiki/Method_cascading (in languages that do not natively support cascading), concretely by having each method return this https://en.wikipedia.org/wiki/This_(computer_programming) ( self).
If you read Fowler's post on the subject all the way to the end, you will see his comment: I've also noticed a common misconception - many people seem to equate fluent interfaces with Method Chaining. Certainly chaining is a common technique to use with fluent interfaces, but true fluency is much more than that. He also points out that returning self (this) is not a hard requirement: You should choose your return type based on what you need to continue fluent action. Here's the link again for anyone who missed it the first time. https://www.martinfowler.com/bliki/FluentInterface.html Even the Wikipedia page you linked to says: Note that a "fluent interface" means more than just method cascading via chaining; it entails designing an interface that reads like a DSL, using other techniques like "nested functions and object scoping". which is one paragraph after the bit you quoted at me. The bottom line is that what the OP was asking for: result = list(something).append(spam).append(eggs).append(cheese) is not a fluent interface, it's just boring old method chaining like we already do with strings. Fowler's example of a fluent interface is worth reading: he uses it to build a compound object (an Order) using a lightweight DSL. The critical part is Fowler's comment: Probably the most important thing to notice about this style is that the intent is to do something along the lines of an internal DomainSpecificLanguage. Indeed this is why we chose the term 'fluent' to describe it, in many ways the two terms are synonyms. The API is primarily designed to be readable and to flow. If it doesn't read fluently as a domain specific language, it isn't a fluent interface. Merely chaining function calls or method calls or infix operators or statements is not sufficient. If it were sufficient, then everything is a fluent interface and the term is meaningless.
A fluent interface is built on top of method chaining: you can't (easily?) have a fluent interface without chaining, but you can have chaining without a fluent interface. See Fowler, who came up with the term and ought to know what he meant by it :-)
I don't really see what you mean in Fowler that would allow "non-fluent" method chaining,
Are you arguing that *any* time you chain two method call in a single expression, that's a fluent interface? If not, then you can answer that question yourself: think of a case where chained methods aren't sufficient, and you have your answer: string.strip().split().count("spam") That's just a mechanical rearrangement of postfix or prefix functional syntax with no additional DSL features. string strip split "spam" count count(split(strip(string)))("spam") It's not a DSL and it's not "fluent" in any meaningful sense, only in the trivial sense that we can read source code in languages we can read. Mutability is also a side-issue. Being written in Java, Fowler's "Order" example would probably mutate a Customer object and an Order object, but one could use a fluent interface with Haskell where each call created a new object and disgarded the previous one. The aim of fluent interfaces is to create a domain-specific interface, not just to squeeze things into a single expression by chaining dots. The *mechanism* they use to implement that DSL is method chaining together with objects designed to cooperate together to give the semantics needed in your domain. For example, in Fowler's example, the "with" method knows about products, and can create an order line and add it to an order. In the same way that people mistakenly think that the Rule of Demeter is about "never put two dots in the one expression", people are mistaken if they think fluent interfaces are *merely* about chaining methods. It's like arguing that Python's command line and REPL is a GUI, because the terminal window is displayed using pixels. Well yes it is, but that's not sufficient to make a GUI.
but I'll stipulate there's some sentence I missed or read wrong. Nowadays, the term is in more widespread use, and exactly what Fowler meant in 2005 isn't really essential.
The DSL part gets to the core of what distinguishes a fluent interface from merely chaining methods (or pipes, or function calls). If there's no difference between "fluent interface" and "method chaining" then why do we need two terms for them, and how can we say that one is implemented with the other? -- Steven