,---------------. | Contributions | `---------------´ ,------------------------------------------ | 2025-08-07 14:44:10 JimPeterson wrote: | comment - `?:`? | see: https://forth-standard.org/standard/core/Colon#contribution-397 `------------------------------------------ The standard defines `?DO` and `?DUP` as companions to `DO` and `DUP` in instances where it may not be useful to perform the operation, and merely skipping it is more convenient than forcing the user to have to check for such instances. I find myself wondering if it would not also be useful to have a companion for `:`, possibly called `?:`, which performs similarly. There are a modest amount of words that are not standardized, but that the community generally understands, and several systems implement, such as `-ROT` and `RDROP`. This situation will always persist. Even if such words become standardized, I'm certain the community will develop new words that are useful but yet to be standard. My own system has efficient implementations for words like `SWAP-` and `CELL-`, but in order to write reasonably portable code, I have to include lines at the top of the form: ``` [UNDEFINED] SWAP- [IF] : SWAP- SWAP - ; [THEN] [UNDEFINED] CELL- [IF] : CELL- -1 CELLS + ; [THEN] [UNDEFINED] <= [IF] : <= > 0= ; [THEN] ``` On systems where such words are already implemented, the implementation is almost definitely more efficient that the Forth-code equivalent, so one would not wish to just blindly re-define them, hence the `[UNDEFINED]`/`[IF]`/`[THEN]`. With a word like `?:`, the intent would be that it behaves almost exactly like `:` except that if the word was already defined previously, the newly provided definition is dropped. This would reduce the above to: ``` ?: SWAP- SWAP - ; ?: CELL- -1 CELLS + ; ?: <= > 0= ; ``` I have a reference implementation for such a word, as shown below. It assumes that `:NONAME` definitions get compiled at `HERE`, and so rewinds `HERE` when skipping a definition. Some other, system-dependent behavior may be beneficial if this is not the case. ``` FALSE VALUE SKIP-DEF : ; POSTPONE ; SKIP-DEF IF ( orig-here :noname-def ) DROP HERE - ALLOT FALSE TO SKIP-DEF THEN ; IMMEDIATE \ usage: `?: -ROT ROT ROT ;`, for instance, will \ define `-ROT` only if it's not already defined. : ?: >IN @ BL WORD FIND NIP IF \ skip definition: DROP HERE TRUE TO SKIP-DEF :NONAME ELSE \ back up and define >IN ! : THEN ; ``` My only concern with this implementation has to do with instances such as: ``` ?: RDROP POSTPONE R> POSTPONE DROP ; IMMEDIATE ``` Since calling `IMMEDIATE` after a potential `:NONAME` definition can be bad. To avoid trouble, the following code could guard against it, if prepended to the above: ``` \ make sure `IMMEDIATE` is ok \ to call even after a `:NONAME` TRUE VALUE IMMEDIATE-OK : : : TRUE TO IMMEDIATE-OK ; : :NONAME :NONAME FALSE TO IMMEDIATE-OK ; : IMMEDIATE IMMEDIATE-OK IF IMMEDIATE THEN ; ``` If there are other colon-like words in a system, they would also need to be sure to set `IMMEDIATE-OK` to `TRUE`. Any thoughts? ,---------. | Replies | `---------´ ,------------------------------------------ | 2025-08-07 15:29:04 EricBlake replies: | comment - `?:`? | see: https://forth-standard.org/standard/core/Colon#reply-1483 `------------------------------------------ The idea is clever, but is there enough standard practice to be worth turning it into a formal proposal for addition to the next version of the standard? Some benefits I see in your idea over [IF]/[THEN] parsing - you still end up parsing the body of the definition using normal Forth - which means that an implementation with a smart compiler that can detect things like bad syntax (an IF without a matching THEN, a DO ... EXIT LOOP that forgot to UNLOOP, etc) can flag problems in your replacement definition even though the definition is ultimately discarded. Remember, [IF]/[THEN] has to do a purely word-based search without regards to which words might have parsing semantics, and throws away words until encountering the end of the condition (the smart compiler never sees what those words were for flagging syntax errors, and you run into weird issues where parsing changes, like `1 [IF] \ not really a comment, [THEN] 2` leaving 2 on the stack because \ was thrown away rather than being used as a parse-to-end-of-line during the [IF]). Looking at your suggested reference implementation, I think it would be better to have ?: use MARKER if it learns that its subject word already exists, and then have the replacement ; invoke the just-created marker, as a more portable way to undo effects of compilation of a :NONAME body created after the marker, rather than trying to do a negative ALLOT which only rewinds back to HERE but fails to clean up any other side effects in other memory regions. With a marker, you can even use : instead of :NONAME to parse the definition that will be discarded (since invoking the marker will re-expose the original definition) - but even so, invoking a marker probably leaves it unspecified whether there is a most recent definition and therefore ambiguous to use IMMEDIATE after your replacement `;`; so your solution probably still has to keep magic in place for IMMEDIATE to be a no-op if you just undid the definition of a word. Also, would it be any better to use [DEFINED} or SEARCH-WORDLIST instead of FIND to learn if the definition being attempted will shadow an earlier definition? Then again, a solution using only Core seems nice. ,------------------------------------------ | 2025-08-07 18:20:42 EricBlake replies: | comment - Ambiguous conditions | see: https://forth-standard.org/standard/core/IMMEDIATE#reply-1484 `------------------------------------------ > The standard allows the implementations where IMMEDIATE affects the last definition in the compilation word list. It is the only reason for the following ambiguous condition (from 16.3.3): > An ambiguous condition exists if a program changes the compilation word list [...] before modification of the behavior of the most recently compiled definition with [...] IMMEDIATE It turns out that this sentence also covers the case of: ``` MARKER m : foo 1 ; m IMMEDIATE ``` since the execution of m changes dictionary pointers. In the process of forgetting both m and foo, it is also making it impossible to portably determine whether there is a most-recent compilation that immediate can affect. But it would be nice to state that directly in the text for IMMEDIATE and/or MARKER, rather than having to refer to the Search word set to find that out.