Ever since the creation of null and blank, it's been desirable to go between them.
The word I first chose for NULL => BLANK! was TRY. It would pass through anything else:
>> try null
== _
>> try _
== _
>> try 1020
== 1020
The reverse operation of converting BLANK! => NULL was called OPT; again passing through everything else:
>> opt _
; null
>> opt null
; null
>> opt 1020
== 1020
It may seem mysterious to ascribe such a trivial operation to a high-value word like TRY. But it made sense at the time, because nulls were rather unfriendly:
>> if not x: find "abc" "d" [print "Assignment goes okay..."]
Assignment goes okay...
; null
>> if not x [print "But word fetching has problems"]
** Error: X is NULL
TRY provided a way to disarm the result:
>> x: try find "abc" "d"
== _
>> if not x [print "Didn't find it"]
Didn't find it
But as it turned out: NULL was trying to do too many things, being fused with the notion of "unset variables". It was a good start on having non-valued states... we just needed more!
Once VOID arrived it was no longer necessary to error on null variable accesses, and this particular usage of TRY was not necessary.
But...It Was Actually On The Right Track...
FIND is a bad demonstration. But TAKE on empty block is actually a good one from that erroring-on-nulls era:
>> x: take []
; null
>> if x [print "-some- error around here seems right..."]
** Error: X is NULL
>> x: try take []
== _
>> if x [print "-some- signal for saying it's okay seems right..."]
-some- signal for saying it's okay seems right...
But we see it's suboptimal, as now X is valued... and conflates with if we had written take [_]
. It also seems the need for the TRY in this case should have been independent of whether you stored the result in a variable or not.
Definitional errors come in and clean this mess up... so that TRY can defuse the take and give you the NULL state you want:
>> x: take []
** Error: TAKE of EMPTY block, use TRY if you meant to do that
>> x: try take []
; null
But Now How Do We Interconvert BLANK! and NULL?
You're still going to come across situations where you pick a blank out of a block, where it's representing a null intent. And it's illegal to compose or append nulls to blocks / etc, so how do you convert them?
Meet REIFY and DECAY.
>> reify null
== _
>> decay _
; null
We actually have new members of this family: the conversion of isotopes to quasiforms.
>> true
== ~true~ ; isotope
>> append [a b c] true
** Error: Cannot append ~true~ isotope to block, use REIFY for quasiform
>> append [a b c] reify true
== [a b c ~true~]
>> append [a b c] as word! true
== [a b c true]
I think the words are a nice coupling...and even the same number of letters!
What Will OPT Do Now?
I'm not sure. I'm actually pulling back a bit on the idea of saying that what happens in PARSE is a TRY... because in essence there's always a built-in "TRY" during parse. Rules are raising definitional errors, but that's just swept under the rug by the parse process itself. So OPT kind is kind of a different shade of meaning there.