Add Result.{get_ok',error_to_failure}#13720
Conversation
nojb
left a comment
There was a problem hiding this comment.
I am generally in favour (modulo naming).
The more general function value_or : ('a, 'e) result -> ('e -> 'a) -> 'e discussed in #13696 could also be considered, but I think it makes sense to special-case these two functions since they connect the exception and result-based error-handling mechanisms so they are particularly useful.
get_ok': personally, I think the name is justified by the semantic proximity toget_ok.error_to_failure: not very clear in my opinion (it reads as if the function returned a value representing failure). Perhapsvalue_or_failwith(which would be coherent with the hypotheticalvalue_or)?
|
The name The compound |
|
I mentioned in point 2. here why I think this is not a good name in context1. More generally I'm not very fond of these "hardcoded boolean expression in identifier" schemes when you can avoid them. For reading, I do not want my mind to have to evaluate a boolean expression in an identifier. I want the function to mention the important case it operates on, With that constraint, once you factor in the I can see the point you are making about value conversion but again, the context just shows you that this function can't possibly produce a Footnotes
|
|
I'm happy with let get_ok = function Ok v -> v | Error _ -> invalid_arg "result is Error _"
let get_ok' = function Ok v -> v | Error e -> invalid_arg e
let error_to_failure = function Ok v -> v | Error e -> failwith e(it's not a veto, I'm just struggling to put an approval on it) |
Well in the In the conversion to let x = parse_integer s |> Result.get_or_failwith in
let x = parse_integer s |> Result.error_to_failure inLiterally I read the second |
Also perhaps I should add that the reason why I use this function postfixed is for the similarity with let* x = parse_integer s in Now if suddendly I'm annoyed by it (e.g. in loops) I simply switch to let x = parse_integer s |> Result.error_to_failure in And somehow to me it reads the same, except for what happens on error. |
|
I understand @dbuenzli's argument that identifiers do always occur in some context, but My subjective take: when I'm struggling like this with naming, it's usually a sign that the function itself is probably wrong. The suggested One character saved in the second case. Footnotes
|
I think trying to read what you propose speaks by itself. It doesn't read in the right order and doesn't even have the right structure on how it is being used in practice. Once more… this PR proposes to add a combinator to convert A few other names have already been proposed So far for me the only other contender is Footnotes
|
Converting your linked example to the one using a hypothetical let x = parse_integer s |> Result.value_or failwith inPersonally, I can't see any problem reading this line of code, and I can't see why this approach is inferior to the proposed |
But that's not the order you proposed in your message. I see many, they have already been stated.
Well for that you will have to read the whole discussion at which point you will also likely read this comment. I don't feel like wasting everyone's time by rehearsing the same arguments over and over. |
nojb
left a comment
There was a problem hiding this comment.
One month later, can we make progress here?
Personally I am willing to go with @dbuenzli's choice of names (but then again I try hard not to get distracted in disussions about names). The semantics of the functions themselves seem OK as well. Thus I am approving the PR, with the understanding that a second official approval is needed.
LGTM
|
Two months later. I'd really like to get rid of my extremely tiny and thus extremely annoying to carry, Here are a few possibilities:
Note I'm not interested at all about the generalizations or the weak names with embedded boolean logic that have been proposed. More on the position here and of course in the whole discussion where the name has been thoroughly justified (describe what happens on |
|
FWIW, I'd really appreciate if this is merged and part of the next OCaml release. |
|
Ping. It's getting cold, and when it gets cold everyone rushes. No need for this to be part of the rush, these are trivial, low maintenance, highly useful additions, that have been painstakingly justified and first proposed on the 23rd of december 2024. I'm not sure why taking a decision on something as trivial and explicit as:
seems so difficult for upstream. |
|
I cannot convince myself that I like these functions. The premise is that people write code like match foo with
| x::xs -> ...
| [] -> Error "The list must be non-empty"and then we need to fight over the name of I think that We already have a type that allows for structured exceptions: match foo with
| x::xs -> ...
| [] -> Error (Invalid_argument "The list must be non-empty")and then we only need to squabble over the naming of one function of type |
Since I'm the one who has been justifying things over and over for hours to get these trivialities in. Could you perhaps justify more thoroughly as to why this "a better design" ? That's just words you wrote.
Could you perhaps explain us how better off the code would be ? Because no, in general that's not what happens. Most of the time you convert to strings much before because:
Doing so with polymorphic variants is painful and most of the time not useful (been there, done that) and I can't imagine doing so with Using Ok so @gasche won't approve because he has his own (rather dubious I must say) ideas on how errors should be handled in practice. Is there perhaps any other maintainer that shares my view on this and that is willing to approve this ? |
|
I agree that |
lthls
left a comment
There was a problem hiding this comment.
I don't think we gain anything by delaying this PR. The code is tiny, the semantics are clear, the names are as good as we're going to get, and we have evidence of these functions being used in practice.
Which is the very basis of good error reporting.
I don't get what you say here. If the error information is complete then you rather don't want to package it as an In general at API boundaries I dont want any of this:
Calling your hypothetical Now the string error monad is a very fine error monad to work with for building error messages for cli tools and application/server level logging. And For example when you process a sequence of elements in a failstop manner in a recursive subfunction, rather than painfully thread the error monad accross the recursive function calls just use Also rereading your message I have to stress that this completely misses the mark as to what
The idea of The only difference with |
hirrolot
left a comment
There was a problem hiding this comment.
After @dbuenzli's recent explanations, I now understand the reasoning behind Result.error_to_failure more. Raising a dedicated exception, perhaps with raise_notrace, and then catching it in a single place to turn it into some kind of value/result is a pattern that I use myself in Mazeppa, albeit not with strings. But I can imagine that strings could be genuinely used in a similar situation.
|
Merged. Thank you everyone for participating in the discussion! |
|
Thanks! I think I'm going to take a little |
|
But I have to say, I'm sure 5.4 will become a natural lower bound in the future, it's an excellent vintage stdlib wise. Thanks for all the additions. |
|
"Ah yes, 5.4! A rainy winter but we avoided most of the PR bitrot. And then a nice warm spring which let those stdlib functions grow nicely before the release branched." |
Split from #13696 please read the discussion there before commenting on the names.
Interaction with exceptions
Result.get_ok'this is likeResult.get_okbut takes the opportunity of having astringerror case to directly transfer the message to theInvalid_argumentexception. If we have nice error messages at hand it's silly not to use them for programming errors.Result.error_to_failure, transformsstringerror cases toFailure _exceptions. This is something I often use internally in the implementation of functions. It allows to avoid the error monad in the implementation and just resurface it at the API boundary by simply wrapping the body of the function with a top level| Failure e -> Error eexception handler.