Pipe-forward operator |>


#21

I know, but with your solution one should maintain a zoo of extension functions from project to project for different set of libraries. With appropriate import option it should became less messy.


#22

After fiddling with the problem, I finally wrote those:

fun add(x: Int): Int = x + 1
fun mul(x: Int): Int = x * 12

class Pipe<out A>(val v: A) {
    inline infix fun <B> andThen(function: (A) -> B) = Pipe(function(v))
}

infix fun <T, R> T.into(other: (T) -> R) = Pipe(other(this))

fun main(args: Array<String>) {
    5 into ::add andThen ::mul andThen ::println
}

I think it works quite great without adding anything to Kotlin itslef


#23

You do not need the Pipe class for that. You do always create an object for each result, but why?

infix fun <T, R> T.into(func: (T) -> R) = func(this)

should be sufficient. It looks like that:

fun main(args: Array<String>) {
     args[0].toDouble() into ::sqrt into Double::inc into ::println
}

I think, that would make the operator pretty much useless, because it is just one line of code enabling the rail (or pipe or whatever).


#24

It can be done in many ways, below code worked very well with me:

package hello

fun main(args: Array<String>) {
    var a = 3
    val b: Int = 6


    println("Hello World! doubling, then tripling the sume of $a and $b is " +
            "${(sumx(a,b)
                    next ::dbl
                    next ::trpl
                    )}")

    println("Hello World! doubling, then tripling the sume of $a and $b is " +
            "${(sumx(a,b)
                    .let (::dbl)
                    .let (::trpl)
                    )}")
    println("Hello World! doubling, then tripling the sume of $a and $b is " +
            "${(sumx(a,b)
                    .run (::dbl)
                    .run (::trpl)
                    )}")

    println("Hello World! doubling, then tripling the sume of $a and $b is " +
            "${(sumx(a,b)
                    into (::dbl)
                    into (::trpl)
                    )}")
}

fun sumx (x: Int, y: Int) : Int = x + y
fun dbl (x: Int): Int = x * 2
fun trpl (x: Int): Int = x * 3

infix fun <T, R> T.next(map : (T) -> R) : R = map(this)
infix fun <T, R> T.into(func: (T) -> R) = func(this)

The output is same in all cases, as:

Hello World! doubling, then tripling the sume of 3 and 6 is 54
Hello World! doubling, then tripling the sume of 3 and 6 is 54
Hello World! doubling, then tripling the sume of 3 and 6 is 54
Hello World! doubling, then tripling the sume of 3 and 6 is 54


#25

FWIW, a pipe-forward operator would be very much welcome, and it seems within the idiom of the language.

For example, the coroutine documentation makes references to pipelines – it would be useful to be able to pipe coroutines together
|> coroutine1
|> coroutine2
|> coroutine3


#26

But you can already pipe all relevant coroutine and sequence operations in Kotlin without any special piping operator. This is made possible by extension functions in Kotlin. All the “pipeable” functions in Kotlin idiomatic code are defined as extensions, so you can write s.filter { ... }.map { ... } as a pipeline. Only languages that lack the concept of “extension function” need piping operator to turn regular (non-extension) filter, map, etc functions into a pipeline-looking style.


#27

You can already do it like this with no new declarations and much more readable:

args[0].toDouble()
        .run(::sqrt)
        .run(Double::inc)
        .run(::println)

#28

great thread with interesting ideas. Fwiw, I’ve found the pipe operator useful in other languages. The best I can do is similar to a couple of other suggestions:

infix fun <T, R> T.v(fn: (T) -> R): R {
    return fn(this)
}

(1..10) v { it.filter { it % 2 == 0 } } v { it.map { it * 2 } }  

Nowhere near as elegant as the scala or F# equivalent but it suffices.

It would be very useful to allow for operators that are not limited to the alphanumerics that we are limited with.


#29

But why don’t you just use this?

(1..10).filter { it % 2 == 0 }.map { it * 2 }

Elizarov already pointed out that you can do all of those things already. I mean this is what extension functions are made for.


#30

The last example was not a correct example, if I’m right.

The reason to include something like this is if you have a function which receives one param.

With a option like this, you don’t need to create an extension function for every single function that accepts only one param.
Instead you can just use one function, like into.