Since I feel RAISE of an error is such a superior option, I thought it would be worthwhile to go ahead and retrofit PARSE3 to do this as well.
It really clarifies things. For instance, there was some rather old code that looked like this:
let directives: collect [
let i
if block? prior [
parse3 prior [some [set i: issue! (keep i)]]
]
]
It predates COLLECT being a feature of PARSE3, so it uses regular COLLECT (which lacks the nice properties like backtracking the collected data when rules fail).
What it was trying to do was that you'd have something like:
[#foo #baz #bar a b c d ...]
It would collect any issues at the head of the block, e.g. [#foo #baz #bar]
. There may be no issues at all, in which case the collected result would be an empty block []
But using a plain SOME rule is bad way of conveying this. The PARSE is failing, and that failure is just treated casually. At the very least, this should be an opt some
.
Then we have the issue of not reaching the end, and that being "okay". But the okayness is nowhere in the rule formulation, meaning the person reading this code is in the dark. It needs something like a to <end>
or an accept (okay)
.
let directives: collect [
let i
if block? prior [
parse3 prior [
try some [set i: issue! (keep i)]
accept (ok)
]
]
]
Of course, modern UPARSE is much slicker (!)
let directives: parse prior [accept collect opt some keep issue!]
It caught the fact that ABOUT has been broken for years!
If you look way back in the history of open source Rebol, you will find an simple dialect for printing out the ABOUT information. The dialect looked like this:
make-banner [
*
-
"REBOL 3.0 [Alpha Test]"
-
= Copyright: [system/build/year "REBOL Technologies"]
= "" "All rights reserved."
= Website: "www.REBOL.com"
-
= Version: system/version
= Platform: system/platform
= Build: system/build
= Warning: "For testing purposes only. Use at your own risk."
-
= Language: system/locale/language*
= Locale: system/locale/locale*
= Home: [to-local-file system/options/home]
-
*
]
By means of the generation function MAKE-BANNER it would transform into an asterisk-laden box, with the substitutions made:
**************************************************************************
** **
** REBOL 3.0 [Alpha Test] **
** **
** Copyright: 2014 REBOL Technologies **
** All rights reserved. **
** Website: www.REBOL.com **
** **
** Version: 2.101.0.4.40 **
** Platform: Linux libc-x64 **
** Build: 7-Nov-2014/18:50:11 **
** Warning: For testing purposes only. Use at your own risk. **
** **
** Language: none **
** Locale: none **
** Home: ./ **
** **
**************************************************************************
Simple though it is, I think it is a pretty good example of how you can "rethink" your problems in a dialect to pare down your code to express your intent.
But it was based on a PARSE rule with no check for whether the parse had actually succeeded or failed! This means that when paths like system/version were changed to tuples like system.version the parse rule stopped working, and only printed the top part of the box in Ren-C:
**************************************************************************
** **
** REBOL 3.0 (Ren-C branch) **
** **
** Copyright: 2012 REBOL Technologies **
** Copyright: 2012-2021 Ren-C Open Source Contributors **
** Licensed Under LGPL 3.0, see LICENSE. **
** Website: http://github.com/metaeducation/ren-c **
Even though the tests would run ABOUT to make sure it didn't crash, it didn't catch this half-output-box.
I Was Convinced, Now I'm More Convinced
As I go through the system and fix/clarify these issues, there's no regrets on the change.
Red's lack of definitional errors means they couldn't embrace the idea even if they wanted to. Triggering an error when a parse didn't succeed would be conflated with any other error that happened inside the parse (typos etc.) so trapping errors would be impractical. Oh well...