-
Notifications
You must be signed in to change notification settings - Fork 256
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’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Dafny 4 Suggestion: Adaptors to fix usability of update with failure #2436
Comments
I like both of your ideas. |
Don't you mean that all uses of such features within a method or function must use the same generic failure-compatible (FC) type? Could you add to the description of this issue, a Dafny example where you want to use two FC types in one callable (a method or function), and ideally also how you would currently work around it by splitting the callable up into two, to make it more obvious what the current pain point is? |
Yes - but then that applies to any chain of calls that pass the FC value up the chain. |
But when you call a method that returns a FC type, you can convert that value to another FC type, so at any point in the chain you're free to use the FC type you prefer, no? As mentioned, I think it would be good to have a use-case in the issue description, so it's more obvious what the downsides of the current approach are. |
I think the issue is real but narrow. I thought the same as you @keyboardDrummer , but there's one case where you can't conveniently do this, and it's when you have a method that returns two parameters, one of which is an outcome and the other of which is a regular value. I didn't know that it was possible to call such a method with Normally when I want to cast a method result I do something like this:
The problem is that in @davidcok's example you can't do this. @davidcok in your original message you mention that this is an issue with the update-with-failure statement and the related expression; can you give an example of that issue for expressions? I see the problem with statement, but unless I'm missing something it seems to be specific to methods that return multiple values, and hence shouldn't be an issue with functions, correct? |
Thanks for the explanation, but even in the multiple return value case, you're still free to use method m() returns status: CalleeFCT, value: int {
return CalleeFCT.Fail("Error!", 9), 42;
}
method mm() returns CallerFCT {
var status, k := m();
if status.IsFailure() then {
return CallerFCT.Failure(status.error)
} else {
return CallerFCT.Success
}
} Also, what's the reason to support |
Sorry for the delay in getting back to this. The problem is not only with multiple return values. It is simply this: in the current structure, in using the
But then you want to call a library method someone else has written, which uses, say, the
That is, you have to do a manual conversion from one failure alternative to another. That is no better than having to remember to check status values after every C function one calls. There seem to me these options:
I'm not wedded to using an attribute, and certainly not to the name, but do think that Dafny would be better for large projects if the language supported some sort of FCT conversion, specifiable at the call-site and independent of either the caller or callee FCT. @keyboardDummer said: But when you call a method that returns a FC type, you can convert that value to another FC type, so at any point in the chain you're free to use the FC type you prefer, no? Yes you can always make the call using @cpitclaudel : Yes I did gloss over let-or-fail expressions. In the case of @cpitclaudel : The problem is not just with multiple out parameters, as I described above. The problem is when the construction of a Failure value in the caller differs from the construction used in the callee's PropagateFailure method. @keyboardDrummer said: Also, what's the reason to support If Dafny is going to support I actually prefer to keep the error status separate from the return value, and so I much prefer the library's
And it works consistently with multiple outputs. But that is a style and preference question that is not the point here. |
Just focusing on the issue with expressions:
See above with
Not quite, again, because you can chain the cast. You suggest doing it with an attribute, but for expressions it can be done with a member function instead.
The example you gave with
I think it is? I agree that One last note:
I write this like the following:
For me what this suggests is that we really want to be able to chain functions after methods; then you could write this:
But: I agree that this doesn't solve the multi-return case any way. Finally @keyboardDrummer:
Potentially performance? Dafny code using tuples is pretty slow in C# at least. |
My overall point was that there needs to be a way to convert one failure type to another at the call-site. I'm not wedded to any particular solution. Your use of MapFailure does precisely that, though as you point out it does not work with multiple returns. Your example does take a function as an argument, so it is not restricted to known FCTs. I see these issues:
Actually, if PropagateFailure took a function argument and there was a syntactic way to say to use a particular function, that would be an implicitly applied MapFailure, which would meet my overall goals in the context of the current language. |
The revision of the Dafny library adds functions to convert among FC-types. This may be sufficient for most use cases. The one that is not covered is a method returning an FC-type where the calling method has a different FC-type. For this, currently, one needs to capture the callee's FC-value (with :=) and then do a conversion manually. We might want to put in some syntax that makes this more concise, but we can wait to see if there is any user use case driving such syntax. It will only arise if users are using source code from multiple sources that use differnt FC-types that then need to be converted among. |
The update-with-failure statement (and related expression) has a significant usability problem, which is that all the uses of such features within a program must use the same generic failure-compatible (FC) type. This is a usability problem for any major project that incorporates source code and libraries from multiple sources.
I can imagine a variety of kinds of FC types that are plausibly used: the Result and Outcome types that are in the library code and variations of those that held more failure data, such as location of error and more specific information about the error than just a message.
The root of the problem is that the PropagateFailure method in a FC type is what converts a failure value in the callee to a failure value in the caller, but the PropagateFailure method is a member of the callee . That is, the callee has to know the the FC types used by all its callers and a caller can only use callees with compatible FC types.
A better approach would be to have the caller have multiple methods that constructed a caller FC value from values of a variety of input FCTs. Dafny would pick a method of the caller FCT that had the right calleeFCT->callerFCT signature and use it to convert the failure value as needed. No PropagateFailure method is needed on the callee side. An identity function could be implicitly an option.
This would look like
Even here though the caller has to know how to extract failure information from a callee. And if the FC type of the caller is a library type, only a fixed set of conversions can be built in, even after adjusting the Dafny language to look for more than one conversion. But this would allow a library to accommodate a few known styles of use -- for example both the value-carrying Result and the non-value-carrying Outcome currently in the library.
A more general approach would be to permit a non-ghost conversion function to be indicated at the call site for any conversion that is not handled by the above. That would look something like this:
The type of the value of the :convertFailure attribute is (C) ~> (C'), where C is the callee FCT at a the call-site and C' is the caller FCT of the enclosing method. The function is only called when IsFailure? is true.
The desugaring of
is
So I have these recommendations:
This is not necessarily a breaking change -- if there are no attributes and no matching caller-side functions, then PropagateFailure would be used. If there already is by coincidence a matching caller-side function, then if it took precedence over PropagateFailure, there would be a change in behavior.
The text was updated successfully, but these errors were encountered: