emojicode / emojicode

😀😜🔂 World’s only programming language that’s bursting with emojis

Home Page:https://emojicode.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Add error handling for methods without a return value

june128 opened this issue · comments

commented

"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.

commented

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❗️
commented

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 🚨.

commented

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.

commented

@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.

commented

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 ?

commented

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.

commented

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

commented

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.

commented

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 🍇🚧🍠🍉 ?

commented

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).

commented

This is mostly implemented and will be part of Emojicode 0.9