These simple pipeline function can be added to arrow-syntax
How different is this from andThen? If it applies the same sequentiality but on top of types and not functions it might also be called andThen?
andThen lazily chains functions. then eagerly chains values. It's an infix alias for run really.
And what's the value on the discarding overload? Can you give a sample use case where it's useful?
What I'm trying to achieve is emulating the pipeline operator (|>) in F# and OCaml (http://roscidus.com/blog/blog/2013/10/13/ocaml-tips/#handy-operators), so we can have expression bodies that looks like comprehensions for imperative code that isn't monadic.
data class User(val address: String)
fun User.queryUpdateUser(): Query = ...
fun Query.updateDatabase(url: Url): Unit = ...
fun String.makeToast(): Unit = ...
With then pipe:
fun updateUser(address: String): Unit =
address
then ::User
then ::queryUpdateUser
then { updateDatabase(Url("paco.works")) }
then "Finished"
then ::makeToast
Without then pipe:
fun updateUser(address: String): Unit {
val query = User(address).queryUpdateUser()
val url = Url("paco.works")
query.updateDatabase(url)
"Finished".makeToast()
}
Edit: just remembered you cannot take function references from extension functions. Second revision:
fun updateUser(address: String): Unit =
address
then ::User
then { queryUpdateUser() }
then { updateDatabase(Url("paco.works")) }
then "Finished"
then { makeToast() }
Open for discussion
What would that same code looks like with?
infix fun <A, B, C> ((A) -> B).andThen(g: (B) -> C): (A) -> C = { a: A -> g(this(a)) }
fun updateUser(address: String): Unit =
::User
andThen { it.queryUpdateUser() }
andThen { it.updateDatabase(Url("paco.works")) }
andThen { "Finished" }
andThen { it.makeToast() }
(address)
From a value we can derive a function. The identity function. I agree with Jorge and think this should be called andThen or rename andThen to then. I like it but dislike the different names.
I like what you're trying to achieve here, but in the end it's chaining dependant operations vs chaining dependant values. Even if this is eager this time, it looks like the concept behind it is the same than andThen so would probably be easier for the end user to have both named equally.
I also find the discarding variant a bit "side effect-y", since discarding previous values on the chain probably means that you're promoting those steps to modify some external state. Otherwise why would you call them if you're planning to discard them? But this is just thinking loud probably, since it might not be a problem. At the end of the day side effects are needed in many controlled scenarios.
We have a pipe function already
fun main(args: Array<String>) {
splitter(filterBills(calculatePrice(Quote(20.0, "Foo", "Shoes", 20))))
// equivalent to
Quote(20.0, "Foo", "Shoes", 20) pipe ::calculatePrice pipe ::filterBills pipe ::splitter
}
That solves it then 馃懐
@pakoito @MarioAriasC The only issue I am running into with the pipe function is that I do seem to be able to put the statement on multiple lines like:
val res =
Quote(20.0, "Foo", "Shoes", 20)
pipe ::calculatePrice
pipe ::filterBills
pipe ::splitter
Is there any way to make this possible?
I'm not sure I understand your request. You're able or unable to put those statements on multiple lines? If the parser is unable to find them you'll need to use .pipe(::calculatePrice) and it's outside of our control.
Ahh yes that works:
val res =
Quote(20.0, "Foo", "Shoes", 20)
.pipe(::calculatePrice)
.pipe(::filterBills)
.pipe(::splitter)
thanks for the clarification.