Looking back at a quote from Nenad I have historically taken issue with, we may not actually disagree as much as it first seems. He said:
"Redbol languages are based on denotational semantics, where the meaning of every expression needs to have a representation in the language itself. Every expression needs to return a value. Without unset! there would be a hole in the language, several fundamental semantic rules would be collapsing, e.g.
reduce [1 print ""]
=>[1]
(reducing 2 expressions would return 1 expression)."
We actually agree on the part in bold. The twist is that he goes on from what I think is the true part ("needs to have a representation"), and conflates it with the idea that the direct use of an expression's result must behave as something you can put in a block.
I'm saying you should always be able to get to a value to put in a block... but you might need an additional step to get it. That could be an operation like LIFT
or REIFY
, which gives a value you can put in a block, but can then be reversed to provide an antiform back.
In Ren-C, PRINT returns "TRASH!"... a SPACE antiform. This would cause an error, but not if you LIFT it:
>> reduce [1 print ""]
** Script Error: Invalid use of ~ antiform
>> reduce [1 lift print ""]
== [1 ~]
These are the problems that isotopes are designed to solve! Without formalizing an isotope mechanism in the language, your choices are:
-
Write your code manipulating Rebol structures in another language (like C or Red/System)...which is inherently "meta" and can handle the oddness of these states.
- (People should be suspicious when problems with the language are addressed by not using the language!)
-
Make usermode code struggle with refinements like /ONLY that pushes the oddness off of the values and forces generalized code to shift into a different handling mode.
It's a significant enough problem area to be worth attacking with a generalized solution, that keeps the oddness on the value states where it belongs. People should have an "a ha" moment about that when seeing things like REPLACE (which has ALL semantics by default in Ren-C)
>> replace [[a b] a b a b] [a b] [c d e]
== [[c d e] a b a b]
>> replace [[a b] a b a b] spread [a b] [c d e]
== [[a b] [c d e] [c d e]]
>> replace [[a b] a b a b] [a b] spread [c d e]
== [c d e a b a b]
>> replace [[a b] a b a b] spread [a b] spread [c d e]
== [[a b] c d e c d e]
As I say, when Red tries to solve these kinds of problems without isotopes--e.g. claiming UNSET! is "just another type"--it's like they're doing complex math without complex numbers. You will hit limits when trying to do nontrivial things.