11
\$\begingroup\$

I'm seeking greater clarity around whether we currently allow functions to throw exceptions, and if so, under what circumstances. There seem to be contradictory posts on this topic—several highly upvoted and all with positive scores—scattered across Meta.

No exceptions

If people answer with functions, they should take into account that the purpose of functions is to reuse them without restarting the program. If you really want to throw an error, answer with a full program.

The quote above comes from the accepted answer (+26/-3) to Should submissions be allowed to exit with an error?. That answer, together with the accepted answer (+44/-8) to Do function submissions have to be reusable?, proposes the following rules for function submissions (as I understand them):

  • Functions must be reusable arbitrarily often.
  • Any error during a function call that terminates the call, but not the enclosing program, is permissible.
  • Any error during a function call that either (i) unconditionally terminates the program or (ii) terminates the program unless specially handled in the external scope (e.g. wrapped in the language's equivalent of Python's try/catch block) is not permissible.
  • If an exception is thrown during function definition (i.e. before the function can be called), the language's equivalent of a try/catch block must be included in the byte count.

The accepted answer (+20/0) to What are our rules about additional code accompanying function submissions? further clarifies that

  • Once a function has been defined, calls to that function must not rely on any additional code.

I interpret these posts to state unequivocally that functions may not throw exceptions, even if those exceptions could be caught.

Except...

More recently, three answers to Default for Code Golf: Input/Output methods explicitly refer to functions throwing exceptions. Their titles are fairly self-explanatory:

The first of these rules also appears to have been repeated under Default policy for ouput in decision problems:

These defaults would seem to allow functions to throw exceptions under certain circumstances.

Questions

I can see a few possible explanations for the apparent contradiction:

  • An old consensus banning functions from throwing exceptions has been superseded (or partially superseded) by a newer consensus allowing this.
  • There is, in fact, no consensus.
  • I've failed to grasp a meaningful (but possibly subtle) distinction between the scenarios covered by the cited groups of posts.

My questions:

  1. Has a blanket ban on functions throwing exceptions gone out the window?
  2. If so, are there cases in which throwing an exception from a function is still considered unacceptable, and if so, why? A non-trivial example that falls outside the scope of the 'default I/O' posts cited above is this solution to Complete a sequence using its distances. The function relies on an exception to terminate an indefinitely repeated process, but may be called arbitrarily often by appending a rescue clause.
  3. If functions are allowed to throw exceptions (whether generally or only under specific circumstances), to what extent are the rules concerning function reusability and accompanying code still in force?
\$\endgroup\$
2
  • 4
    \$\begingroup\$ Good question. I always felt that function outputting via throwing/not throwing is somewhat controversial (despite the existence of the default I/O post at +31), so I explicitly excluded it from my challenges and made it a "bonus" submission (under a "proper" one) in my answers. \$\endgroup\$
    – Bubbler
    Commented Feb 24, 2022 at 0:18
  • \$\begingroup\$ @Bubbler I usually do so only as inter function to allow fast return, but this is code-golf and, not practial call methods are used \$\endgroup\$
    – l4m2
    Commented Feb 26, 2022 at 12:37

1 Answer 1

12
\$\begingroup\$

Functions throwing exceptions should be allowed

based on this brief chat conversation

As long as the exceptions can be caught, this seems like a perfectly legitimate way of producing output or stopping the function's execution. We allow things like await f() or f(1)(), so try {f()} catch (x) {...} seems perfectly reasonable.

I wouldn't consider these functions to be non-reusable. I mean, f(1)(2) also isn't reusable if you ignore how it's supposed to be called and cause an error by doing something like f(1, 2), but we ignore that since it's assumed the function is called the correct way (with a try/catch if exceptions can be used for output).

This allows for more interesting possibilities, like quickly returning a value in a recursive solution as mentioned by l4m2, or compactly indicating things like invalid input for relevant challenges.

The only situations this shouldn't be allowed, aside from when the challenge author explicitly disallows it, is if the answer uses exceptions output, but then also ignores the limitations of the machine when that can also cause an exception. E.g., relying on a maximum call stack error for one case, but also assuming that the stack can be arbitrarily large in another part of the solution.

\$\endgroup\$
7
  • \$\begingroup\$ Related to your last paragraph \$\endgroup\$
    – emanresu A
    Commented Mar 1, 2022 at 2:53
  • \$\begingroup\$ 1. Is this answer intended to apply only when exceptions are used for output? (The title seems broader, but I read the first sentence and last paragraph as narrowing the scope to exception output.) Specifically I'm asking in relation to the example I gave in the question; in that example the exception occurs separately from the output. 2. There was (is?) another consensus that global variables can't be assumed to be reset between function calls. Does your second paragraph allow me to say 'The correct way to call my function is f(); SOME_GLOBAL_VAR = 0'? \$\endgroup\$
    – Dingus
    Commented Mar 7, 2022 at 23:49
  • \$\begingroup\$ @Dingus I mean, an exception sort of has to be output. Once a function throws an exception, the function is no longer running. So the specifics of the error, or just the fact that an error was thrown, is a sort of output. I think that's a lot different from the global variables case, because the try/catch is (IMO) clearly just a (fairly natural) way to handle the different ways the function can produce output or otherwise stop running, like await in JS, while something like SOME_GLOBAL_VAR = 0 is much more unambiguously offloading the answer's intended behavior onto the user calling it \$\endgroup\$
    – rydwolf
    Commented Mar 7, 2022 at 23:55
  • \$\begingroup\$ I agree with you about globals. I guess what I'm really getting at is whether there's an unambiguous way to draw the line between what is and isn't acceptable. (The ban on any additional code was perhaps heavy-handed, but it was definite.) I think we also agree that an exception is a sort of output, but my question is, can I have a function that prints to STDOUT (where this is the desired output) before throwing a catchable exception to STDERR? We allow this sort of thing for full programs, but previously it wasn't allowed for functions. \$\endgroup\$
    – Dingus
    Commented Mar 8, 2022 at 0:18
  • \$\begingroup\$ I think that would be fine, yeah. I'll clarify my answer to show that. \$\endgroup\$
    – rydwolf
    Commented Mar 8, 2022 at 0:38
  • \$\begingroup\$ Here's a question: For an answer to "return the number doubled", is this valid? What about this? \$\endgroup\$
    – pxeger
    Commented Mar 8, 2022 at 8:01
  • \$\begingroup\$ @pxeger I'd say yes. \$\endgroup\$
    – rydwolf
    Commented Mar 8, 2022 at 15:01

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .