Add error handling for methods without a return value
june128 opened this issue · comments
"error handling" means "everything to make errors possible (error types, checking for errors and so on)" in this issue. This makes formulations easier.
⭐️ Proposed change
Add error handling for methods, which don't have a return value.
New emojis, which indicate that error handling is going on, would make error handling for these methods unmistakable.
Reference: algorithm-archivists/algorithm-archive#471 (comment)
🤔 Rationale
Imagine the following two methods:
📗 Increase someNumber by the specified value. 📗
❗️ 🆙 summand 🔢 🍇
someNumber ➕ summand ➡️ 🖍someNumber
🍉
📗 Return someNumber multiplied with the specified multiplier. 📗
❗️ ⏺ multiplier 🔢 ➡️ 🔢 🍇
↩️ someNumber ✖️ multiplier
🍉
Now - for the rationale - add some error handling:
Let us imagine that we don't want the methods to accept negative values and that an error should occur then.
Error Handling for the Second Method
For the second method we can just use the inbuilt error handling:
- Create an error enumeration:
🦃 ⏹ 🍇
🔘 🔽
❗️ 🔡 ➡️ 🔡 🍇
↪️ 🐕 🙌 🆕⏹🔽❗️ 🍇
↩️ 🔤The provided value can't be negative.🔤
🍉
↩️ 🔤🔤
🍉
🍉
- Implement the error handling for the method:
📗
Return someNumber multiplied with the specified multiplier.
Only allow positive multipliers to have a reason to drop an error.
📗
❗️ ⏺ multiplier 🔢 ➡️ 🚨⏹🔢 🍇
↪️ multiplier ◀️ 0 🍇
🚨🆕⏹ 🔽❗️
🍉
↩️ someNumber ✖️ multiplier
🍉
After we have done this, we can call the method and handle potential errors the following way:
💭 Here the method ⏺ doesn't return an error.
🥑 returnValue ⏺ myTest 10❗️ 🍇
😀 🔡 returnValue 10❗️❗️
🍉
🙅♀️ error 🍇
😀 🔡error❗️❗️
🍉
💭 Here the method ⏺ does return an error.
🥑 returnValue ⏺ myTest -1 ❗️ 🍇
😀 🔡 returnValue 10❗️❗️
🍉
🙅♀️ error 🍇
😀 🔡error❗️❗️
🍉
Error Handling for the First Method
For the first method (which doesn't have a return value) we can't use the inbuilt error handling - we need to use optionals to mimic error handling.
This can be done in the following way:
- Use the same enumeration as above.
- Mimic error handling by using an optional:
📗
Increase someNumber by the specified value.
Only allow positive summands to have a reason to drop an error.
📗
❗️ 🆙 summand 🔢 ➡️ 🍬⏹ 🍇
↪️ summand ◀️ 0 🍇
↩️ 🆕⏹🔽❗️
🍉
someNumber ➕ summand ➡️ 🖍someNumber
↩️ 🤷♀️
🍉
Now we can check for errors by evaluating whether or not the optional holds a value:
💭 Here the method 🆙 doesn't return an error.
↪️ 🆙 myTest 10❗️ ➡️ return 🍇
😀 🔡return❗️❗️
🍉
💭 Here the method 🆙 does return an error.
↪️ 🆙 myTest -1❗️ ➡️ return 🍇
😀 🔡return❗️❗️
🍉
Conclusion
Even tho we can mimic error handling for methods without a return value, we need to do so using optionals. This isn't perfect for a few reasons:
- Having the return value be an optional doesn't necessarily mean that error handling is mimicked. Therefore the (mimicked) error handling isn't identifiable from the methods signature.
- With the error handling for methods with a return value, the
🥑
clearly indicates an error check. For methods without a return value, checking for the error is just evaluating, if the optional holds a value. It isn't clear that this particular check is a check for an error. - We also can't use
🚥
for methods without a return value.
🕺Example
Provided in the Rationale.
Thanks for opening this issue. My thoughts after reading through the issue:
To declare a method that requires error handling we could use the following syntax, which is already used by initializers:
❗️🚨⏹ 🆙 summand 🔢 🍇 💭 ...
It is worth considering adapting this syntax for all methods. So the second method would become:
❗️ 🚨⏹ ⏺ multiplier 🔢 ➡️ 🔢 🍇
Methods with that might err would still return an error type (🚨...). Consequently, the error type of a method with no return would be an error type composed of the specified error enum type and the no return type. An error type whose contained type is no return can obviously not be unwrapped with 🍺.
Both of these methods could be used with 🥑, which should not allow a variable name if the contained type is no return:
🥑 returnValue ⏺ myTest 10❗️ 🍇
😀 🔡 returnValue 10❗️❗️
🍉
🙅♀️ error 🍇
😀 🔡error❗️❗️
🍉
🥑 🆙 myTest 10❗️ 🍇
😀 🔤Hurray!🔤❗️
🍉
🙅♀️ error 🍇
😀 🔡error❗️❗️
🍉
🚥 could be used for both methods:
🚥 ⏺ myTest 10❗️
🚥 🆙 myTest 10❗️
The question now is: Do we want to use the the same emoji for the new error type which is composed of an error enum and the no return type?
Sure, it would be nice to have 🚨
as the universal emoji for the error type, but I think the fact that you can then unwrap one error type with 🍺
, but not the other, could lead to mistakes and confusion.
So maybe we should use another emoji for the new error type that is similar to 🚨
.
We should avoid adding emojis to the language due to Backwards Compatibility and Readability (Another emoji to remember). That doesn’t mean it’s off the table, but let’s consider this:
We could as well allow 🍺 for the error type with no return. This would be a way to make sure, that a method did not return an error and otherwise abort the program. Example:
🍺 🆙 myTest 10❗️ 💭 It’s super important that 🆙 succeeds
This is similar to Swift’s try!
(Disabling Error Propagation).
The difference, though, is that in Emojicode you don’t have to deal with errors if you don’t like. If you call a method with a return value and are not interested in the return, you can ignore whether an error occurred. That’s possible with my proposal above too. And of course, when implementing error handling with optionals too.
But if we started requiring the user to deal with errors, 🍺 for no return errors would make even more sense. Dealing would be either unwrapping the error or using 🥑. As far as I can see now, this would also mean disallowing to store errors in variables.
It would also be great for errors to "go up the callstack". Take these methods (with error prefixed method) :
🦃🍕🍇
🔘🍔
🔘🍟
🍉
❗️🚨🍕🐶🍇
🚨🆕🍕🍔❗️
🍉
❗️🐱🍇
🐶🐕❗
🍉
❗️🐯🍇
🐱🐕❗
🍉
For now, only 🐱
can handle 🐶
's error. It would be great to be able to let 🐯
handles 🐶
's error because maybe 🐱
do not cares about 🐶
's error but 🐯
does.
Also, "type catch only" would be a plus too.
Being able to catch "errors based on their type" and if the type is not supported, go up the callstack.
@rozaxe What would be the use of 🚨🍕
in your example? 🐱
could raise a 🍕
error too, so shouldn't it be attributed with 🚨🍕
as well? On the other hand, this allows methods to raise multiple types of errors. So the attribute would need to take several error types, I guess.
@thbwd For instance, I am writing an unit test framework. I am developping methods "assertTrue, assertEquals, assertFailWith, ..." à la Emojicode. Those methods will raise an error on failure. Users will call those methods, and I do not want the users to check if there were an error, that would defeat the purpose of the framework. So, with the previous example, 🐶
is an assert method that can fail in the framework, 🐱
is the methods end users implement and use 🐶
to assert there functions are correct. And then 🐯
is a sandbox in the framework, calling 🐱
and that makes sure assert functions pass and if not, tell users where it fails.
For this example, I do not want to prefixe 🐱
with 🚨🍕
because it is really verbose, and I want to delegate the errors handling to something else (the unit test framework here) and end users should not worry about errors handling.
I do not want to prefixe 🐱 with 🚨🍕 because it is really verbose
So what would be the rule on when to use 🚨 annotations with methods then? I don’t get the purpose if methods can raise errors without such annotations. Then we wouldn’t need them at all.
Indeed you are right, there is no need to annotated methods with 🚨
in this case.
Also, I am not a huge fan of "mandatory raise exception" like Java. I prefere C++ exceptions, and maybe add 🚨
annotation for documentation only ?
I don’t think this would fit Emojicode’s overall design. Emojicode is explicit everywhere in order to provide great safety. If every method can potentially throw an exception a lot of that safety goes away in my opinion. Thinking of C++ it’s so easy to ignore or to miss that a function might throw. I don’t want that in Emojicode.
Java’s throws
tends to get excessive though, I agree with that. So some general throws
like Swift has it seems reasonable.
What would be the complete type of an anonymous block 🍇🍉
(callback) which calls "method that can throws errors" ?
Also, I do like the language Kotlin (superset of Java, with optional and mutable / non mutable variables, like Emojicode) and they went with "no checked exceptions" and they provide examples and citations : Kolint on checked exceptions
The type of callable that might throw an error would be 🍇🚨🍉
, I guess. At leats that seems straightforward to me.
The Kotlin example makes sense. But in my opinion it is partly due to a somewhat strange design of the Java standard library, which made checked exception so hard to deal with.
After consideration I propose the following:
-
Errors are declared with 🚧 instead of 🚨 after the return type if one is present:
❗️ ⏺ multiplier 🔢 🚧 ⏹ 🍇 ❗️ ⏸ multiplier 🔢 ➡️ 🔢 🚧 ⏹ 🍇
-
The error type (the type after 🚧) must be a class inheriting from 🚨.
-
The process of returning an error is called raising.
-
The additional error operator 🔺 is added. If an error is raised by the expression it is applied to, it raises the error from the current method. E.g.
🐇 🍠 🚨 🍇 💭 ... 🍉 🐇 🌶 🍠 🍇 💭 ... 🍉 🐇 🥦 🍠 🍇 💭 ... 🍉 🐇 🍊 🍇 ❗️ ⏺ ➡️ 🔢 🚧🌶 🍇 💭 ... 🍉 ❗️ *️⃣ ➡️ ➡️ 🔢 🚧🥦 🍇 💭 ... 🍉 ❗️ 🐃 ➡️ 🔡 🚧🍠 🍇 💭 Both 🌶 and 🥦 can be raised from this method as they inherit from 🍠 🔺⏺🐕❗️ ➕ 🔺*️⃣🐕❗️ 💭 ... 🍉 🍉
-
🥑 is changed so it can have multiple handlers for different subclasses of 🚨
❗️ 🦞 🍇 🥑 🐃🐕❗️ 🍇 🍉 👮♂️ broccoliError 🌶 🍇 💭 ... 🍉 👮♂️ chiliError 🥦 🍇 💭 ... 🍉 🍉
-
🚥 is removed.
-
Errors must be checked and cannot be ignored. Methods/initializers do not return an error type. The possibility of an error occurring is a property of the method/initializer itself.
Pros:
- Methods without return can raise errors.
- Errors can go up the call stack more easily but still only if desired explicitly.
- A method can return different error types thanks to polymorphism.
- Errors are objects that can carry additional information.
Cons:
- Casting to classes is actually
O(n)
, which is what 🥑 has to do. 🚧🚨
could have valid use cases but can also be abused easily. The compiler should check whether the declared error type is actually the “smallest common type” and issue a warning otherwise.
Great !
I suppose so callable will be typed as 🍇🚧🍠🍉
?
Yes, basically that’s right. However, I’m not yet sure how to handle a case where an error raising closure is passed to a method like the 🍨🐰 (map).
This is mostly implemented and will be part of Emojicode 0.9