-
Notifications
You must be signed in to change notification settings - Fork 55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add monad comprehensions via binding block with bind extension function #13
Conversation
Wow this is a very interesting concept. Is this inspired from an existing Result/Either impl? If so I'd like to look over how it compares to an established implementation.
This is particularly interesting. I wasn't aware that the compiler contracts would have any effect on performance? Looking at result4k's flatMap, they are doing the same thing as in this library and not applying any fewer object allocations than I do. So I can't see any other explanation other than the inclusion of contracts. result4kinline fun <T, T使, E> Result<T, E>.flatMap(f: (T) -> Result<T使, E>): Result<T使, E> =
when (this) {
is Success<T> -> f(value)
is Failure<E> -> this
} kotlin-resultinline infix fun <V, E, U> Result<V, E>.andThen(transform: (V) -> Result<U, E>): Result<U, E> {
contract {
callsInPlace(transform, InvocationKind.AT_MOST_ONCE)
}
return when (this) {
is Ok -> transform(value)
is Err -> this
}
} On a wider note, if you have done some benchmarking with JMH or similar then I am very open to adding a benchmark framework to this repository. Let me know if that's something you'd like to contribute to. |
* @sample com.github.michaelbull.result.bind.ResultBindingTest | ||
* | ||
*/ | ||
inline fun <V, E> binding(crossinline block: ResultBinding<E>.() -> V): Result<V, E> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we should make this function name more specific, for example resultBinding
or bindResult
, as binding
feels too generic. Let me know if you have a good proposal for this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Upon further inspection, it looks like a library called bow
for swift calls this function "binding", so let's keep it that way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah nice, yep bow is the counter part to arrow so this makes sense!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but i agree, perhaps it is a bit too generic, and I agree resultBinding
might be the best way to phrase what is is - my only issue is it becomes quite a long word to use for what is essentially a keyword.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
interestingly if you go far back enough in the arrow repo (back to when it was called kategory) they had considered calling bind
-> await
. but might have been a good choice to not confuse this as being something async or relating to coroutines
@michaelbull thanks for fast and detailed response! 馃グ
So this is the output of having investigated using arrow, then having gone to see this talk at kotlin conf last year: https://www.youtube.com/watch?v=pvYAQNT4o0I Arrow added this functionality via Monad Comprehensions. My intial attempt was to basically strip back their coroutine code to work strictly for result type (i mention the benchmark results of that in the description of npryce/result4k#3). Edit: Something you may notice reading arrows monad comprehension documentation. They show in their examples of using fx bind that they override operator (_) = resultThatWIllFail() // skipped by compiler |
also regarding
sure I can open a pull request with benchmarking added. SOmething I never tried to do actually was compare any of these libraries to the internal kotlin result type. Might be interesting! |
Added in 9bcaa97 |
nice! really like the |
I replaced the |
also I can rebase #14 now to point to new master |
@michaelbull do you think it would make sense to add this functionality to the readme? I can open a proposed change if you'd like? Up to you 馃槃 |
Yeah, I think a section titled "monad comprehensions" or something would be good. If you could also add the links to the example implementations (e.g. swift's bow lib) to emphasise where the inspiration comes from that would be helpful. Is there a way to get the benchmarking to output the results in a markdown formatted table? I would like to add benchmark results to the README (ran on my desktop) but don't see an easy way of displaying the data. |
@michaelbull hmm the only output I know of is in might need to write a script to generate some pretty output of that |
pretty cool! |
It's a logarithmic scale, you can still compare the raw numbers appropriately |
Hello!
Thought I would open a pull request to add monad comprehension like syntax for this result type.
Full disclosure: before i knew about this repo I had looked to adding this feature for result4k: npryce/result4k#3
Wasn't getting a response there so I've moved on 馃槄 that's when I started looking around and found this very cool repo!
For that pr I did quite a bit of micro benchmarking with jmh. The main reason for not moving forward with arrow's either type for me is how poor performing it is. The apis are also very unstable (still not at a 1.0 release). So it was important for me to choose another lib that was performant.
With my (possibly contrived) test I have benchmarked and compared flatmapping versus using my proposed bind syntax for this repo. It came out a little slower:
One thing to point out is that comparing this library to Result4k, the ops/ms is significantly slower in this repo for flatmapping (i get around 250,000 - 280,000 ops/ms in this repo, compared to almost 450,000 ops/ms in result4k on my machine). I suspect its related to the use of contracts and more object creation in this repo. Regardless, its still pretty fast! just thought its interesting.
Anyway, hope your open to the proposal and keeping well in these strange times!