let result = "hello"
|> doubleSay
|> capitalize
|> exclaim;
vs. result = "hello".doubleSay().capitalize().exclaim()
I certainly prefer method chaining here. let result = "hello"
|> doubleSay
|> capitalize
|> exclaim;
vs. result = "hello".doubleSay().capitalize().exclaim()
I certainly prefer method chaining here. order = %{amount: 123, customer_id: 45}
result = order
|> Orders.validate()
|> Database.save()
|> PubSub.publish()
whereas with method chaining you can only call methods of the object being passed around.- It's more flexible on input types: You can deal with sum types without polluting your classes with flags.
- It's more flexible on execution order: You can shortcut things, you can define your operator in a way that it runs some things twice, you can jump over some function (if the typing allows).
- It's more composable: You can intercalate this with other operators.
- It's data: You can input the sequence into a function, rewrite it and execute the new sequence, interpret it and create some documentation, run some offline verification without executing the code, etc.
result = exclaim(capitalize(doubleSay("hello")))
and the pipe makes it simpler to read, especially if you're chaining some functions that can take additional arguments let result = "hello"
|> sayTimes(3)
|> capitalize
|> exclaim x |> f ->. f(x)
So, applying that, it would let you transform something like exclaim(capitalize(doubleSay("hello")))
to your first example. But it can't do anything with the 2nd example, because those are not functions with arguments, they're all 0-arity method calls.More generally, I don't think this operator makes much sense in a language that's mostly object-oriented. I made one in Scala a while back and toyed with it, but even there it's pretty useless because the libraries follow a very object-oriented coding style. I've really only seen it shine in a language that has strong ML roots and tends to stick to them.
In other words, they are 1-argument function calls. The "this"/"self" parameter is implicit in many languages, but otherwise it's not different from a normal function parameter. In the "method chaining" version of the example, each method call returns a (reference to) a temporary object which is passed as the next method call's "this" parameter.
More specifically, if the language won't allow me to do both:
foo.method(x)
and method(foo, x)
and have them behave the same way, then I submit that, regardless of what's going on behind the curtain, a n-arity method is not really the same thing as a (n+1)-arity function in that language.foo.method(x)
Is equivalent[1] to
Bar.method(foo, x)
[1] in the absence of monkey patching on foo