Programming can get messy. So messy in fact that function calls can get so embedded that they become difficult to follow.
Take the following nested functions into consideration:
getResponse(notify(persist(validate(request))));
Pipe-forward operator lets you pass an intermediate result onto the next function.
Let’s take another look at the code snippet above rewritten using the pipe operator:
I think for the cases this is needed, the following might be close enough:
fun main(args: Array<String>) {
val r = "hello" next ::length next ::double
println(r)
}
fun length(s:String) = s.length
fun double(i : Int) = 2 * i
infix fun <T, R> T.next(map : (T) -> R) : R = map(this)
That’s why I said “close enough”. I think the added value for such feature can be considered minimal while it adds substantial complexity to the language.
A functor is defined upon it’s context and leaves it context intact. E.g. a map function on a List class results in a List with transformed values, the context List remains the same. This is “just” applying a function.
Back to the topic, I think adding new operators to the Kotlin should be done rarely. If you do fancy all kinds of operators there are already plenty of languages that fill that spot, with Scala being the obvious one. I agree with sodiaan that, when familiar with pipe-forward operators, this example is very readable. But omitting parameters can be considered as unreadable too.
I find the following almost just as easy to read (and write) without the need for any special language constructs.
val v = validate(request)
val p = persist(v)
val n = notify(p)
return getResponse(n);
We actually have such a context but hidden, it is something like IO monad.
But of course I do not agitate to introduce monads into Kotlin, but just want to emphasize that the problem bay be generalized in some way to make |> or other like that more general mechanism in Kotlin,
Here is the gist with some not very nice implementation of pipe forward operator(unary plus in a gist), it is steel required to have some aid from Kotlin lang designers to make it elegant in usage.
In short, we need an operator ‘|>’ which actually converts a:
(T) -> T1
to:
<T>.() -> T1
and have a nice syntax support for usage in a Kotlin lang because it is not very nice to see it as:
I like the idea of having the ability to call any function like an extension. There are plenty of Java libraries with classes containing static utility methods which may be called like this.
In addition to @vjache’s comment it would be great to have some option to import function as an extension. I had this idea for a long time. As soon as I read the reference I constantly feel a lack of such functionality. Unfortunately I’m not a Kotlin user, just a fellow traveler. As for me let constructs are not elegant in such cases.
But of course there are some issues to be discussed about like:
allowing not only the first argument to be treated as receiver;
Maybe I’m wrong, but you can do that by creating a little (inline) non-member helper function that makes the right calls. Some of the issues with imports (where it is limited) could be handled the same way.
If we do ANYTHING along these lines, I recommend going vjache’s route, especially since plain functions and extension methods are both just implemented as static methods in Java. In reality, the only difference between a function call and a method call is how you pass in the primary parameter.
Now, personally, I don’t think this needs to be implemented in any way at all. If we really need to string along some function calls, we can make our own versions as extension methods and chain dot operators. We certainly don’t need a pipe operator.
Why is it so complex to parse the first parameter as this-instance?
Isn’t this what Java internally actually does, passing this as first parameter and accessing it via byte-code instruction aload_0?
This would be a backwards incompatible change, because it would create overload resolution conflicts or change the way overloads are resolved in cases when you have a method and an extension function with signatures differring by one parameter in Kotlin 1.0 code.