Target: ooRexx 5.3 trunk (patches made against r13265; the files involved
are identical in r13271).
Component: Interpreter, interpreter/RexxClasses/CoreClasses.orx; the
json and yaml extensions; the test suite.
Name: .Boolean in the patches; .Logical would do as well, and fits
Rexx's own vocabulary ("logical value"). The choice is left open.
In Rexx, .true and .false are the strings "1" and "0". That is right
for Rexx, and nothing here proposes to change it. But it means that no code can
tell .false from 0, or .true from 1 by its value.
Some code must, though: anything that writes a format or talks to a language
with a boolean type. Each such package has invented the same answer, an object
that behaves as "1" or "0" everywhere and can still be recognized:
json.cls has JsonBoolean (.json~true, .json~false), so that.json~toJSON writes true/false and not 1/0.yaml.cls has YamlBoolean. Its documentation says it follows "the sameJsonBoolean in json.cls". The code is a copy: about 80 linesmakeJSON,makeYAML) and small differences that have already crept in (the 88.900= is computed).So there are several copies of one idea, and one already has a bug in common:
\.json~true and \.YamlBoolean~true both give 1, because their UNKNOWN
forwards the message "\" to .true, and that hits the bug reported
separately (RexxInteger::notOp).
One class in the core, with what JsonBoolean and YamlBoolean share:
.Boolean~true and.Boolean~false. NEW is private."1" and "0" everywhere: IF,WHEN, SELECT, the logical operators, DATATYPE(x, "O"), string methods,SAY, concatenation (makeString, string, and UNKNOWN forwarding to theisA(.Boolean) tells them apart from plain strings.=, \=, <>, >< compare logical values (Comparable,compareTo), across classes (.json~true = .Boolean~true is true).value returns "1" or "0"..Boolean~of(logical) returns the instance for a logical value.Boolean~of(a > b)), and raises 34.900 for anything that is not 0 or 1."\"), so that the class does not dependhashCode follows the value, so an instance works as a Table index.activate runs for it). SoJsonBoolean becomes:::class "JSONBoolean" public subclass Boolean
::method makeJSON unguarded
if self~value then return "true"
return "false"
and YamlBoolean the same with makeYAML. Their public behaviour does not
change: .json~true, .JsonBoolean~true, isA(.JsonBoolean), makeJSON and
the YAML equivalents all stay. What changes is that both are now also
isA(.Boolean), and that \.json~true gives 0.
A package that needs a boolean (a writer, a database binding, a bridge) checks
isA(.Boolean), and works with JSON's and YAML's values, and any future ones,
without knowing about JSON or YAML. A true read by json~fromJSON can go
straight to Python, .NET, Lua or JavaScript as that language's true.
.true and .false do not change.JsonBoolean and YamlBoolean keep their names, class attributes and\ (it was wrong), and ==.json~true == "1" gives 0 today, identity; 1 with the patch). SeeBoolean (or Logical) is.Boolean, while.json~true is an instance of the core one).MethodArgs.testGroup creates an instanceNEW, so .Boolean goes into its list of.Singleton and .StackFrame already are), with its two.Boolean or .Logical.== and \==: the patch makes == a strict comparison of the values as.Boolean~true == "1" is true), as for a string. JsonBooleanYamlBoolean do not define == today, so for them it is identity."1"", but it can beidentityHash.)~of is the right name for the factory method.boolean-interpreter.patch: the class in CoreClasses.orx; json.cls andyaml.cls reduced to subclasses.boolean-tests.patch: ooRexx/base/class/Boolean.testGroup (new, 11 tests)MethodArgs.testGroup change.boolean.cls: the same class as a standalone package, for use until (orResults, built from the r13265 source tarball on Linux x86_64 (gcc 13), with
this patch and the one for RexxInteger::notOp:
| Tests | Failures | Errors | |
|---|---|---|---|
ooRexx/base, r13271 unpatched |
14369 | 5 | 0 |
ooRexx/base, patched |
14380 (the 11 new ones included) | the same 5 | 0 |
json.testGroup, json_02.testGroup, yaml.testGroup, patched |
314 | 0 | 0 |
The 5 failures are the same tests in both runs (File.testGroup and
Stream.testGroup, file permissions: the suite ran as root in a container).
Without the MethodArgs.testGroup change, that group fails to load
("Object "The Boolean class" cannot accept private message "NEW"").
Anonymous
One more consequence, which I think strengthens the proposal.
json.clsandyaml.clsrecognize booleans by their own classes:json.clsthrough themakeJSONmethod (emitValue), and throughisA(.JsonBoolean)in its XML output;yaml.clsthroughisA(.YamlBoolean)(in the scalar emitter and inscalarType). So with thepatch as it is, a plain
.Boolean~trueis written as1by both: it is alogical value, but not their logical value.
If the writers check
isA(.Boolean)instead, any logical value that knows itis one is written as
true/false, whoever created it: JSON's, YAML's, or abridge's. For example, a Python
Truethat arrives in Rexx as.Boolean~truewould go out to JSON astruewith no extra code. That is thepoint of having one base class instead of one class per package.
The change is four lines, attached as
boolean-writers.patch, on top ofboolean-interpreter.patch:Result, on the patched build:
json.testGroup,json_02.testGroupandyaml.testGroup: 314 tests, 0failures, 0 errors.
On 17.10.2026 09:41, Josep Maria Blasco wrote:
Indeed. However, these are problems of the other language which define explicit/strict boolean types! Therefore an ooRexx bindings all of a sudden must distinguish between a JSON/YAML string with the value "0" string from a JSON/YAML false and a JSON/YAML string with the value "1" from a JSON/YAML true. This becomes significant when creating string renderings that get stored e.g. in a file and get read from a file. So, if ooRexx reads a JSON/YAML file and wants to reproduce it, it must remember, wether 0 was a JSON/YAML string or a JSON/YAML boolean. That is the reason, why the bridge needs to define a JSONBoolean or a YAMLBoolean class! This is not because of ooRexx lacking a Boolean class! The JSON implementation for JSONBoolen (I made sure that the YAMLBoolean is based on that) that I did made sure that even if on the ooRexx side JSONBooleans (or YAMLBooleans) appear, that these values can be used as plain ooRexx strings "0" and "1", such that the context on the ooRexx side determines whether the value gets used as a Rexx string or interpreted as a Boolean by the oo/Rexx context. (Whether a string value is to be used as a Boolean depends on the context, on the use of these string values, e.g. in the context of an "IF booleanValue THEN ..." or "WHEN booleanValue THE..." keyword statement or in the context of a &, or && concatenation where its operands must be Boolean values, i.e. the strings "0" or "1" in oo/Rexx.)
It is the same as with arithmetics: in Rexx/ooRexx you can do arithmetics if the Rexx strings contain numbers, otherwise you will get an error at runtime, if you used strings for arithmetics that are not numbers. Because of that oo/Rexx is very easy. Now, if interacting with other languages that are strictly typed, depending again on the context, you may have to translate strings containing numbers to the target strict type like Byte, Char, Integer, Long, Float, Double, maybe - as in the case of .NET, COM/OLE - even to unsigned integer values, etc.
It may be tempting for strictly typed aficionados to therefore introduce these strict typings into the oo/Rexx language (unnecessarily) by defining all these strict types of other languages also for ooRexx. But then you lose the easiness and simplicity of oo/Rexx that makes it so easy to use and to understand. [E.g., I could not teach object-oriented programming in such a short time anymore, because then you need to first teach the typing system to beginners, who hardly can understand the need for it.]
For bridges to other languages, numbers therefore must be identifiable as numbers such that the bridge becomes able to translate strings with numbers to the target's language matching strict type. Of course it would simplify the bridge implementation (which needs to be done just once, but imply the burden then to the manyfold usages, i.e., to the oo/Rexx programmers), if we started to impose on the ooRexx language the same strict type system as the target language, but for the price to all of a sudden change the soul (and incredible dynamics) of the language into a "dull" ;) strictly typed language.
However, that is the gist of a bridge, and the challenge in the Rexx/ooRexx world: shelter the Rexx/ooRexx programmer from the complexities of the partner language as much as possible. Do not introduce unneeded concepts into the Rexx/ooRexx language that other languages (which copied the concepts from its common ancestors) impose on their users.
The ooRexx-Java bridge was developed for more than 25 years with the goal, to not force any Java concepts onto the Rexx/ooRexx language, and still be able to exploit all of Java. I think I have achieved that, making the bridge easy to use for Rexx and ooRexx programmers.
So, yes, for all the other bridges (.NET, Pythong, etc.), if those languages rely on strictly typed Booleans, the bridge's support needs to create a NetBoolean, a PythonBoolean, etc. (and they should carry the name of the language, such that one can intermix all these bridges with each other).
Well, in oo/Rexx one does not need an explicit class, which always adds a little burden to a language, but one can use the DATATYPE()-BIF with the lOgical argument: it will return .true, if the value is a Boolean, and .false, if not.
Well the second question already implies that a Boolean class gets introduced into the ooRexx language. Again, I can understand how one arrives at this impression and hope to have added a few more views onto what a bridge should do (insulating oo/Rexx from, but making oo/rexx fully ineroperable with the language on the other side of the bridge).
Following one of the Rexx design principles - "keep the language small" - I would not introduce a new class (or any new feature), unless it is really mandatory (a new technical concept must be supported, an important new feature needs to be supported as easy as possible for the oo/Rexx programmer) or helping simplifying a common problem for the ooRexx programmers.
---rony
Ad missing features/errors in the implemenation of JSONBoolean: this can be fixed in the JSONBoolean class, thereby leaving ooRexx unaffected.
One way to still get your idea for bridges: how about defining a package for all bridges that abstracts the common problems with strictly typed languages and tackling it there. So, one could define a Boolean class there and use it for any bridge that has a need for it?
If this was possible/conceivable, this would leave ooRexx unaffected.
There are no errors in JsonBoolean. Incorrect behaviour is a consequence of https://sourceforge.net/p/oorexx/bugs/2117/ -- it's an interprete bug, not a problem with json.cls.
Your idea of abstracting is exactly what we are doing now: boolean.cls is common to all the bridges.
Hmm, the question is, does it any good to ooRexx programmers, if they get an .Boolean class in ooRexx? Or with other words, are there other use cases (besides the need for the bridges which is well understood) that benefit ooRexx programmers?
The mindset about this stance is: "keep the language small".
Hmm, I would see one benefit that you show: classes that get distributed with ooRexx could take advantage of a Boolean class, i.e. the JSON and YAML classes, independent of any bridges. So, if we introduce a Boolean class as you propose to make these two ooRexx classes more bullet proof in a simple manner, then any bridges would be able to take advantage of it. (And adding a class would not change the language per se, just its class library gets one class more.)
Opinions?
---rony
Reworked as discussed on oorexx-devel (17/10/2026):
Booleanis now an extension class distributed with ooRexx, next tojson.cls, not a core class. These attachments replace the earlier ones (boolean-interpreter.patch,boolean-tests.patch,boolean-writers.patch).boolean-extension.patch(against main/trunk r13273):extensions/boolean/boolean.cls(new): the classBoolean, with the two instances.Boolean~trueand.Boolean~false, which behave as"1"and"0"in every Rexx context;.Boolean~of(logical); comparisons throughComparable;defaultNamereturns the value, so that an Array shows1/0inmakeStringandtoString.CMakeLists.txt: copies and installsboolean.clslikejson.cls.json.cls:::requires "boolean.cls";JSONBooleanbecomes a subclass ofBooleanthat keeps its ownmakeJSON; the writer checksisA(.Boolean), so it writes any Boolean astrue/false.yaml.cls: the same, withmakeYAML; the class name is now quoted,"YamlBoolean", to keep its mixed case (Rony's suggestion).Public behaviour of
.json~true,.JsonBoolean~true,.YamlBoolean~true,makeJSONandmakeYAMLdoes not change.boolean-extension-tests.patch(against test/trunk):ooRexx/extensions/boolean/Boolean.testGroup, 16 tests (the class, plus JSON and YAML interoperating through it).Results, built from the r13265 source tarball plus r13272 (bug #2117) and this patch, Linux x86_64:
Boolean.testGroup,json.testGroup,json_02.testGroup,yaml.testGroupooRexx/baseIn one full run,
Message.testGroup'sTEST_NOTIFYalso failed once (expected 5, got 4); it passed three times out of three when rerun, with and without the patch, so it looks like a timing-dependent test.No change is needed in the core or in
MethodArgs.testGroup.Question (maybe answered above but missed): .True and .False are "special" in that ooRexx initializes them in .Environment to 1 and 0 respectively. If I code
a = .true; say a~datatype("O")I get 1. What doI get (assuming we name the class .Logical) if I code.true~isA(.Logical)? To be consistent I would expect to also get 1.I'm not sure we want to do that, by the reasons outlined above by Rony. Rexx is an untyped language, and by proposing a new Logical class we are not trying to bring typing into Rexx. Logical is a utility class that allows us to flag 0 or 1 as logical values for foreign languages or environments where this is necessary. OTOH, .True is undistinguishable from 1. Incidentally, .True, .False and .Nil are no longer .environment entries; there are entries there, but they are fake. Try
and you will get
1 0. The situation you describe was true of Object Rexx, though (checked in ArcaOS)..true, .false, and .nil have become "constant environment symbols", probably to speed up ooRexx as therefore these environment symbols do not have to be looked up anymore. This was done without communication, AFAIK, for 5.0.0. The leftovers are the entries in .local, which until then were looked up. See rexxref.pdf, 1.13.6. Environment Symbols.
Yes, I understand but by introducing the class we may also introduce confusion for the user. Is there a reason the new class has to be visible to the user? ooRexx has a "Numeric" class "under the covers" and not visible to the user. Could this new class be done the same way? (Just wanting to explore all alternatives :-) ).
My impression is that the class should be public, but maybe it should have a more technical name, like LogicalWrapper. Its description should say that this is a technical class, used to interface with other languages, frameworks, etc., that need to discriminate the logical 1 from the numeric 1. Its first clients will be json.cls and yaml.cls, but immediately the WASM port, and the bridges Rexx/Lua, Rexx/Python and Rexx/.Net.
Yes, under the covers for the interpreter itself, hence not publically available.
This class needs to be public in order to be usable by other ooRexx programmers, whoever they are. It simplifies a tricky problem considerably, making sure thereby that the error-proneness (maybe overlooking the potential problems) gets reduced.
Ad name: "Boolean" is what is common in the current main stream languages. "Logical" is very Rexx-ish and would not draw the attention of newcomers to that class.
BTW, once that class exists it may also be an option to use it for ooRexx itself wherever logical values get created (e.g. result of comparisons).
Thank you, Rony. I agree that it has to be public, and I take note of your preference for Boolean; Gil, what do you think?
About using it for the results of comparisons: I would rather not. Today a = b returns the same object as .true, a string, and existing code relies on that, sometimes without knowing it: (a = b)~isA(.String) is 1. If comparisons returned Booleans, those programs would change behaviour silently. It would also bring typing into the core of the language, which is what you warned against. The idea is the opposite: an ordinary logical value stays the string “1” or “0”, and a program uses the class only when it needs to say “this 1 is a logical value” to JSON, YAML or another language.
Yes, it appears that it needs to be a public class so we need to be sure that the documentation is very clear as to its pupose. I still believe that .Logical or .LogicalWrapper (or some other variation) is better than Boolean. Rexx simply does not use that term (it only appears twice in RexxRef, one of which is in the Index(!) and not that much in the PG). Logical, however, is very common in the Rexx documentation and is well understood by the community. I, also, would NOT support its use outside of its intended purpose as it would lead to a whole host of knotty issues (like why isn't .tue/.false an instance of .Logical, etc.).
That's why I proposed LogicalWrapper: it says exactly what it is. Regarding Boolean/Logical, I don't have a strong opinion (so BooleanWrapper would also be fine for me). But I'm slightly inclined to Logical -- because we are wrapping one of our values, which is a logical value. Then the subclasses prepare that for the foreign treatment, and they can be Boolean -- because the foreign environment speaks of Booleans.
That way, the frontier is clearer.
Indeed LogicalWrapper carries both pieces of information, it is about a logical Rexx value and a wrapper for others.
+1
Great. To summarize; LogicalWrapper, and a public class. I will leave the RFE open some few days more and, if there is no further feedback, I'll proceed to implement it.
Sounds good to me.
+1
| no code can tell
.falsefrom0, or.truefrom1.It is trivial to distinguish .true from '1' or similar forms:
Thank you, Erich, you are right: the sentence is too strong. Identity does tell them apart. I’ll reword that sentence in the description: “no code can tell .false from 0 by its value”.
Diff:
Implemented as agreed, in main/trunk r13276-r13278 and test/trunk r13279.
LogicalWrapper is an extension class, distributed with ooRexx next to
json.cls: extensions/logicalwrapper/logicalwrapper.cls, available with
::requires "logicalwrapper.cls". Its two instances, .LogicalWrapper~true
and .LogicalWrapper~false, behave as "1" and "0" in every Rexx context,
and isA(.LogicalWrapper) tells them apart from plain strings.
.LogicalWrapper~of(logical) returns the instance for a logical value.
JSONBoolean (json.cls) and YamlBoolean (yaml.cls) are now its subclasses.
They keep their names, their makeJSON / makeYAML, and their public
behaviour; the JSON writer now writes any LogicalWrapper as true/false.
r13276: the class, and its build and install lines
r13277: json.cls
r13278: yaml.cls
r13279: ooRexx/extensions/logicalwrapper/LogicalWrapper.testGroup, 16 tests
Thanks to Rony, Gil and Erich for the discussion and the name.