Closed elizarov closed 3 years ago
@qwwdfsad I agree with you. I think experience shows that most people won't look at a documentation before they are forced to by an error and even then. The problem is though that making SuccessOrFailure
internal wont work as the whole idea is that the users interact with SuccessOrFailures returned by runCatching
.
IMO the most useful feature SuccessOrFailure
would provide is the ability to properly handle errors in parallel executing code.
val outcomes: List<SuccessOrFailure<T>> = deferreds.map { it.awaitCatching() }
I don't see how this is possible for the user when SuccessOrFailure
would be internal.
What about making the compiler create a warning whenever a function returns a SuccessOrFailure
or expects one as a parameter? In the stdlib we could just suppress the warning.
That way improper use of this will be discouraged without relying on documentation.
@Wasabi375
I don't propose making SuccessOrFailure
internal, only its functional extensions. SuccessOrFailure
will still be used in Continuation
API, but won't have anything which can assist stdlib users. Continuation
is a very low-level API with a few consumers, so it won't be a problem.
In your example, awaitCatching
will be a handwritten extension on Deferred
(with manual try {} catch {}
inside).
But hopefully kotlinx.coroutines
will have better builders and mechanisms for parallel decomposition than map { awaitCatching() }
@qwwdfsad @Wasabi375 You have a point of course. However, real life is a way of compromises. You can't get rid of "rake walkers". Does it mean we should forbid rakes? No one can stop one from doing this:
val nullable: Any? = null
nullable!!.toString() // oops :)
And it doesn't mean there's something wrong with nullable types. There's myriad of ways how one can screw one's code.
The fact is, there is Try
type in Scala stdlib which does the same the SuccessOrFailure
does. And they do not sweat it, because it's the safe and elegant way to handle fallible function.
The only issue is the cost of filling the exception stack at the moment of its creation, that is about 10 times slower than the object creation, yet is not that critical (roughly 1000 ns instead of 100 ns). Which brings the only group of cases when usage of the subject has to be limited. And that's the job of the API documentation.
I also believe the subject deserves a more consistent name.
The Kotlin class to encapsulate success or failure was renamed to Result
and additional restrictions on its usages are now enforced by compiler to allow for future enhancement. Details can be found in the new section of the KEEP document: https://github.com/Kotlin/KEEP/blob/master/proposals/stdlib/result.md#limitations
Those seem like a set of arbitrary restrictions that only make sense from the implementer's perspective. I'm not sure it'll be easy to explain the users why they're in place, specially if they're going to start being used in standard library functions.
Do these restriction exist only to prevent the Railway-Oriented Programming and promote the direct style, or is there any technical reasoning behind them?
@pakoito If the goal was just to prevent ROP, then we would have made it a warning. We made it a compiler error, because if we allow it in Kotlin 1.3, then this code might break in future versions of Kotlin, because of the future changes to the language we might be introducing around Result
type.
P.S. We cannot prevent ROP, anyway. Users are free to use any 3rd-party library with an appropriate result type.
Okay, so binary compatibility. It makes sense, thanks :)
Wouldn't it be a good idea to implement sealed inline class
into the language before 1.3 release, and change Result
implementation, so we don't need to keep the current source and binary interface of Result
forever?
@Dico200 The switch to sealed inline class
in the future should not affect existing binary interface.
Hello all,
This KEEP is more about failure handling in coroutines and parallel decomposition, right? In the first paragraph it speaks about exceptions usages for sequential code.
But Kotlin does not have checked exceptions and this KEEP is not easy to use for failure handling becuse Result
can't be used as public methods and kotlin does not have any syntax support for it (as it has for nulls for example).
Here is my case: some function returns result or error, so it is literally Success T | Failure F
. I want to be sure that user checks for error before using result. In Java I use checked exceptions, so one can't simply ignore error.
In Kotlin exceptions are unchecked, and the only way to implement it is to return null. But with null, I can't return error object.
From my point of view, this is very close to nullability support, but instead of null we must handle error somehow.
For example:
fun getUser(): Result<User, String>() {
return if (is42()) User() else Error("This function is not available on Mondays")
}
///
val user = getUser().onError { error -> User("Unknown") }
val user = getUser().onError { error ->
log(error)
User("Unknown")
}
val user = getUser().onError { error -> throw BadUserException(error) }
val user = getUser().onError { error ->
showMessage("Can't get user: $error")
return
}
Could we have something like that?
Given that Result was scoped as a coroutines-only API, you have to look elsewhere for now. There are several already several existing implementations of Result in libraries, and writing one is quite simple.
Feel free to rip off either (any error) or try (only Throwable) from arrow: https://github.com/arrow-kt/arrow/blob/master/modules/core/arrow-core/src/main/kotlin/arrow/core/Try.kt https://github.com/arrow-kt/arrow/blob/master/modules/core/arrow-core/src/main/kotlin/arrow/core/Either.kt
// shill
Or even better, use arrow-core
instead :D
// shill
@pakoito thank you.
It seems that I should talk to Roman becuase he wrote that such things "Might be an area of further improvement"
I really want to have it in stdlib
Closing the issue as it had been implemented.
This issue is for discussion of the proposal to provide a Standard Library class to encapsulate successful or failed function execution.
Proposal text: result.md