Hi Aaron:
Thanks for the ideas! I think they certainly can mitigate this concern in many situations.
For one situation I'm thinking of, I don't see how to apply these ideas. I have a data structure backed by an Array. I carefully control how the Array is accessed, so if I do my job correctly, I will never give it an out-of-bounds index.
Nonetheless, to access the Array, my code must call Array.get, which returns a Maybe.
Going with approach #1, giving a default value if the Array is accessed out of bounds, worries me that if I DO introduce a bug that causes an Array index out of bounds, this will mask the bug and make it difficult to track down. If I actually made a coding mistake, I want the code to fail as close as possible to the source of the bug.
For approach #2, it's not clear how to scale that to something like, for example, Array index out of bounds.
Here's a decent minimal example: suppose you want to support access to a sequence of values with a sort of "two-way iterator"; let's call the data structure a "Tape" (the actual application I have in mind is a Turing machine).
The Tape is always non-empty, and has a leftmost value at position 0. The client code should be able to access the value at the "current" position, and to move the current position left or right. If it is 0 and they try to move it left, it stays at 0. If it is on the last (rightmost) position of the Array and they move it right, a new default element is appended to the end. So client code can never cause the index to be invalid.
Nonetheless, several times in my code I must access a value in the Tape, and this is how I do it:
getTapeSymbol : Int -> Tape -> Char
getTapeSymbol idx tape =
case Array.get idx tape of
Just ch ->
ch
Nothing ->
Debug.crash ("The tape with contents '" ++ (String.fromList (Array.toList tape)) ++ "' has been accessed with invalid index " ++ toString idx ++ ". There is a bug in the software. Please contact the author.")
The only alternative I see is to return a Maybe Char, and if Nothing ever is returned, that implies there is a bug in my code and I can report it to the user through the "normal" error message (the one that is normally used to tell the user they made a mistake).
I think I just talked myself into thinking that this is the reasonable way to do it. But it does make me long for runtime exceptions for those errors where you know that not only the current function, but also any function that may have called it, really isn't going to be able to do any error-handling or recovery more sophisticated than "print error message and stack trace to screen".
Dave