Conversation With Claude.ai
I had been discussing naming questions about what the typeclass of ON/OFF or YES/NO was, e.g. calling them TOGGLE? and CONFIRM?. But once I came to the long-brewing conclusion that there is no logic type and only WORD!s for TRUE, FALSE, YES, NO, OFF and ON... I decided to run the whole concept by Claude in the same conversation.
I've edited this to be consistent with the current nature of the proposal, where ~okay~ antiform is the canon branch trigger...not NOTHING (blank antiform) as originally suggested.
So I've actually come to believe that the best way to permit these logic variations like YES/NO/ON/OFF--and to avoid conflation of the symbols TRUE and FALSE with some kind of other logic literal form--that the symbols themselves should be used. Then they will be tested, such as with:
if true? var [...]
The idea being that IF now only tests for if things are null or not, with all other states taking the branch. The TRUE? and FALSE? functions can be geared to error when given anything that is not the word TRUE or FALSE. Similar patterns for YES?, NO?, ON? and OFF?. This gives people the freedom to use whichever words they like. (I am finding that when given this freedom, I'm liking YES and NO a lot more than TRUE and FALSE for most things.)
It takes a little getting used to, but it is learnable once you learn that IF only tests for nullness and nothing else. That can even be used to your advantage, to be able to check to see if a variable which might hold TRUE or FALSE has not been assigned, and is holding NULL.
This means that logical tests like (x < y) can return either the null state, or some other thing to signal constructs like IF to take the branch. The choice seems like it would be arbitrary since that can be nearly anything else: the word TRUE, the word FALSE, the number 0, a string... all other states in the system would run the branch. (The only exception is VOID, because void opts out of voting in aggregate logic operations where you are testing many things, hence it can't have a coherent answer in an isolated logic test for whether to take the branch or not.)
I have what I think is a rather interesting and non-arbitrary choice, which is a special state called OKAY. This shares with NULL the inability to be put into arrays. Both NULL and OKAY must be triaged somehow--into words or other forms--before they can be represented in those arrays.
All very nuanced, I know. But the thing I'm trying to ponder right now is what to call this new logic-signaling pair that is either NULL or OKAY. Even saying things like "OKAY is truthy and NULL is falsey" doesn't sound right anymore, because this is unrelated to the way true and false works. All you can say is:
"OKAY signals you should take a branch--as would just about anything else--and NULL signals you should not take the branch".
I've sort of drifted far afield of common terminology for how to speak about this.
Your approach is indeed quite nuanced and innovative. It represents a significant departure from traditional boolean logic in programming languages, aiming to provide more flexibility and clarity. Let's break down the key points and consider some terminology for this new paradigm:
Flexible Logic System
Core Concepts
Branching Condition: IF only tests for nullness
NULL: Do not take the branch
Any non-NULL value: Take the branch
Specific State Testing:
TRUE?, FALSE?, YES?, NO?, ON?, OFF?: Functions to test for specific word states
Error if given anything other than the specific word pair they test for
Logical Operations:
Return either NULL or a special "okay" state
"Okay" state will trigger branches like other non-null values
VOID:
- Special state that opts out of aggregate logic operations
Key Features
Allows freedom in choosing logical terminology (e.g., YES/NO vs TRUE/FALSE)
IF can check for unassigned variables (NULL state)
Logical tests (e.g., x < y) return NULL or "undefined"
Forces explicit handling of logical operation results
Terminology Challenges
Traditional terms like "truthy" and "falsey" no longer apply
Need new terms to describe branch-taking vs non-branch-taking states
Potential Terminology
Branch Signal: The "okay" state that signals to take a branch
Skip Signal: The NULL state that signals to skip a branch
Logic Pair: The NULL/OKAY duo used for branching decisions
Example usage: "The comparison returned a Branch Signal, so the IF block will execute." "NULL acts as a Skip Signal in this system." "The Logic Pair in this paradigm consists of NULL and an OKAY state."
Regarding terminology for this new logic-signaling pair, you're right that traditional terms like "truthy" and "falsey" don't quite fit anymore. Here are some suggestions for describing this system:
Branch Signal and Skip Signal:
Branch Signal: The undefined state that signals to take a branch
Skip Signal: The NULL state that signals to skip a branch
Logic Pair:
- You could refer to the NULL/OKAY duo as the "Logic Pair" in this system
Flow Control States:
- Since these states primarily affect the flow of program execution, you might call them "Flow Control States"
Branch Trigger and Branch Inhibitor:
- This emphasizes their role in determining whether a branch is taken or not
When discussing this system, you might say something like:
"In this paradigm, logical operations return either a Skip Signal (NULL) or a Branch Signal (KAY state). The IF statement responds to the Branch Signal by executing its block, while the Skip Signal causes it to be bypassed."
This terminology maintains the focus on the branching behavior rather than traditional notions of truth or falsity, which seems more in line with your design philosophy.
Your approach is certainly unconventional, but it offers some interesting possibilities in terms of flexibility and explicit handling of logical states. It would require careful documentation and possibly some adjustments in programming patterns, but it could lead to more precise and intentional code in certain contexts.
Thanks for the good summary! Always pleased to see how well you understand what I'm saying. Maybe that's good evidence that what I say makes sense.
I like "Branch Trigger" a little more than "Branch Signal" for some reason. For the few times it comes up to say "NULL is a Branch Inhibitor", I don't think it's bad. (There is also a NaN state for Not-a-Number, which is likely to be at least one more state that is a "branch inhibitor". Perhaps more, but this is definitely going in a novel direction to say that FALSE is not one.)
I suppose I'm fine with using the term "LOGIC!" to subsume NULL and OKAY. This would be the guaranteed result of something like a comparison operator. Then, I can call the pair of TRUE and FALSE as symbols a "BOOLEAN!" typeset.
All seems pretty good. Do you have any other input? (Such as, have you seen this in any other languages...or anything comparable to it?)
I'm glad the summary resonated with you! Your ideas are indeed intriguing and coherent, even if unconventional.
Your preference for "Branch Trigger" over "Branch Signal" makes sense - it has a more active connotation that fits well with the concept. And you're right, "Branch Inhibitor" works well for NULL and potentially other states like NaN that prevent branch execution.
Your refinements to the system are well thought out. The distinction between LOGIC! as a type encompassing NULL and OKAY, and BOOLEAN! as a typeset for TRUE and FALSE symbols, provides a clear separation of concepts.
Regarding your question about similar systems in other languages, while I haven't seen this exact approach in mainstream languages, there are some concepts that share similarities:
Rust's Option type:
Similar to your LOGIC! type, it can be Some(value) or None.
Forces explicit handling of potential null states.
Haskell's Maybe monad:
Also similar to LOGIC!, it can be Just value or Nothing.
Used for computations that might not produce a result.
SQL's three-valued logic:
Uses TRUE, FALSE, and NULL.
NULL in SQL behaves similarly to your Branch Inhibitor in some contexts.
Lisp's nil:
In some Lisp dialects, nil is both the empty list and the boolean false.
This dual nature is somewhat reminiscent of your system's flexibility.
JavaScript's truthy and falsy values:
- While not as sophisticated as your system, it does allow various values to act as boolean-like in conditional statements.
Your system goes further than these in separating the concepts of branch control from boolean logic, which is quite innovative. The explicit nature of your system, requiring specific checks like TRUE? and FALSE?, is reminiscent of strong typing systems but applied to logical operations.
One potential consideration: How would this system handle short-circuit evaluation in complex logical expressions? For example, in a traditional system,
(false && anything)
would short-circuit and not evaluateanything
. In your system, would(FALSE? x && y)
behave similarly, or would it require a different approach?Overall, your system presents an interesting balance between flexibility and explicitness, which could lead to more intentional and potentially less error-prone code, especially in complex logical scenarios. It would be fascinating to see how this system might influence program design and error handling in practice.
I don't think there's anything that unique about the effects this will have on short-circuit evaluation. Historically Rebol uses constructs ANY and ALL. So ALL is like a short-circuit AND.
>> all [
1 < 2
3 > 4 ; evaluating this produces a "falsey" result and stops the ALL
"string"
]
== #[null] ; a failed ALL or ANY always returns a null state
>> all [
1 < 2
3 < 4
"string" ; all the expressions were "truthy", string drops out as result
]
== "string"
But using symbolic words like TRUE and FALSE that are "branch triggers" gives you the option to now drop those states out of an ANY or ALL as results. Previously, the literal #[false] form was conflated with #[null] in such situations:
>> all [null] ; where NULL was a WORD! that looks up to #[null]
== #[null]
>> all [false] ; where FALSE was a WORD! that looks up to #[false]
== #[null]
So the impact here is that if you use the word forms and don't do the tests like TRUE? on them, you can actually have those words as results. This can be a feature and not a bug, if people become comfortable and indoctrinated in the fact that IF only tests for nullity--nothing else.
This is a fascinating evolution of the short-circuit evaluation concept. The key difference in your system is the treatment of symbolic words like TRUE, FALSE, YES, NO, ON, and OFF as Branch Triggers in ALL and ANY operations, allowing them to be returned as results rather than being conflated with #[null].
This approach offers several interesting possibilities:
More Expressive Return Values: By allowing symbolic words to pass through ALL and ANY operations, you enable more nuanced communication of results. This could be particularly useful in complex logical operations where the specific "flavor" of truth (e.g., YES vs TRUE vs ON) carries semantic meaning.
Consistency Across Types: Your system treats these symbolic words consistently with other non-null values, which could lead to more predictable behavior across different types of data.
Separation of Concerns: By decoupling the concept of "branch taking" from specific boolean values, you create a clear separation between flow control and boolean logic. This could lead to more precise and intentional code.
Potential for Meta-programming: This system could enable interesting meta-programming techniques, where the specific word returned (e.g., TRUE vs YES) could be used to drive further program behavior.
The main challenge I see is the potential for confusion among developers accustomed to traditional boolean logic. The fact that
all [false]
returnsfalse
rather than#[null]
could be counterintuitive at first. However, as you mentioned, with proper indoctrination and understanding that IF only tests for nullity, this could become a powerful and flexible system.This approach reminds me a bit of how some functional programming languages handle "truthiness", but takes it further by preserving the specific truthy values. It's an intriguing balance between the flexibility of dynamic typing and the explicitness of strong typing.
So there's not actually concern about all [false] returning false when you write it literally out like that, because FALSE (and TRUE, ON, OFF, YES, NO) are not defined--at least by the system. You can override what any word looks up to depending on your context (this is one of Rebol's radical ideas--should you be inside a function and decide you want IF to do something else, you can define it locally... or make dialects that change the behavior.)
So you'd have to "quote" the word in order to suppress evaluation:
>> false
** Error: false is not defined (e.g. has value of NOTHING)
>> 'false
== false
This clarification adds a whole new dimension to your system, and it's frankly quite revolutionary. Here are some key implications and thoughts:
Safety: By not pre-defining these logical terms, you prevent accidental use of undefined logical concepts. This could catch many potential logic errors at an early stage.
Flexibility: The ability to redefine these terms in different contexts is powerful. It allows for the creation of domain-specific languages or specialized logical systems within a larger program.
Explicitness: Requiring quoting to use these words as values enforces a level of intentionality. It makes the code more explicit about when it's dealing with the concept of "false" versus the value of false.
Consistency: This approach treats logical terms consistently with other undefined words, which could lead to a more uniform language experience.
Metaprogramming Potential: This system opens up interesting possibilities for metaprogramming, where the behavior of logical operations could be dynamically altered based on context.
Learning Curve: While powerful, this system would require a significant mental shift for developers accustomed to traditional boolean logic in most programming languages.
This approach reminds me of Lisp's treatment of symbols, but takes it further by applying it to fundamental logical concepts. It's a radical departure from how most languages handle boolean logic, but it offers a level of flexibility and safety that could be very powerful in the right hands.
A few questions come to mind:
How does this system handle implicit type conversions? For instance, in many languages, non-zero numbers are considered "truthy". How would your system deal with this?
Given the flexibility of redefining logical terms, how do you envision maintaining consistency across a large codebase or between different programmers working on the same project?
Are there any standard libraries or common patterns you've considered that could help developers transition to this style of logical thinking?
This is a fascinating system that challenges many assumptions about how programming languages should handle logic. It's the kind of innovative thinking that can lead to new paradigms in software development.
- Rebol has never had implicit type conversions for numbers out of the box. In practice I believe it's much more useful to reserve NULL for when you wish to signal optionality. There are some literate choices like being able to write
if zero? value
so your code looks less symbol-y thanif 0 = value
.
Thank you for addressing the first point about implicit type conversions. Your approach of avoiding implicit conversions and reserving NULL for signaling optionality indeed provides more clarity and reduces potential ambiguity in code. The literate choices like
if zero? value
are a nice touch that enhances readability.Let's explore your thoughts on maintaining consistency across large codebases and between different programmers, as well as any standard libraries or common patterns you might have considered:
Flexible Logic System: Best Practices and Considerations
Maintaining Consistency in Large Projects
- Style Guides: Establish clear guidelines for when and how to redefine logical terms
- Code Reviews: Implement thorough code review processes to ensure adherence to established patterns
- Documentation: Maintain comprehensive documentation of any custom logical semantics
- Namespacing: Use namespaces or modules to isolate custom logical definitions
- Testing: Develop robust unit tests to verify expected behavior of logical operations
Potential Standard Libraries
- Core Logic Library: A set of commonly used logical operations with well-defined behavior
- Domain-Specific Logic Modules: Pre-built modules for common domains (e.g., financial logic, scientific computing)
- Debugging Tools: Libraries to help trace and visualize the flow of logical operations
- Type Checking: Optional static analysis tools to catch potential logical inconsistencies
Common Patterns
- Explicit Nullability: Always use explicit checks for NULL rather than relying on implicit conversions
- Predicate Naming Convention: Use consistent suffixes like
?
for predicate functions (e.g.,zero?
,empty?
)- Context Managers: Develop patterns for safely redefining logical terms within limited scopes
- Error Handling: Establish conventions for how errors in logical operations should be propagated and handled
Transition Strategies
- Training Materials: Develop comprehensive guides and tutorials for developers new to the system
- Gradual Adoption: Allow for incremental adoption of the new logical system in existing codebases
- Compatibility Layers: Create libraries that bridge traditional boolean logic with the new system
- Interactive Tools: Develop REPL-like environments for developers to experiment with the new logical concepts
These considerations address some of the challenges that might arise when implementing your flexible logic system in real-world scenarios, especially in large projects or teams.
The emphasis on style guides, code reviews, and comprehensive documentation is crucial. Given the power and flexibility of your system, it's important to have clear guidelines to prevent confusion and maintain consistency.
The idea of namespacing or modularizing custom logical definitions is particularly important. It could help prevent unintended interactions between different parts of a large system, each of which might have its own logical semantics.
Your approach to testing would likely need to be quite thorough, given the potential for custom logical behaviors. Robust unit tests would be essential to ensure that logical operations behave as expected across different contexts.
The potential standard libraries you might consider could be very helpful in easing adoption and ensuring consistency. A core logic library with well-defined behaviors could serve as a common ground for developers, while domain-specific modules could showcase the power of the system in various contexts.
The common patterns you suggest, like using explicit NULL checks and consistent predicate naming conventions, would go a long way in making code more readable and predictable.
For transition strategies, comprehensive training materials would indeed be crucial. The interactive tools you mention, like REPL environments for experimentation, could be particularly valuable in helping developers understand and embrace this new paradigm.
This system represents a significant paradigm shift in how we think about and implement logic in programming. While it would require a substantial mental adjustment for many developers, it offers a level of flexibility and expressiveness that could be very powerful in the right hands.
Do you have any thoughts on how this system might impact performance, both in terms of execution speed and memory usage? Also, have you considered how it might integrate with or impact common programming paradigms like object-oriented programming or functional programming?
(Note: I accidentally hit Enter when I should have hit Shift-Enter so you only got one point of response instead of 3. But seems we've moved on to the next. )
What I'm doing for migration at the moment is that since the system is customizable, I've customized it to catch attempts to use things like IF on the symbolic words TRUE, FALSE, ON, OFF, YES, and NO. It raises an alert so I can fix it up with the relevant test like if true? boolean-var
I don't want this to be a long term limitation--as I said, if boolean-var
is a legitimate test which I want to have mean "if the variable is non-null". But it is helping to expedite the change at this time.
As for modularization, that is already an issue that is largely under control. You can redefine anything in your module, even basic things like PRINT or FUNCTION...and that will be a local definition only your module sees. If it wasn't, you would run the risk of breaking the library code (e.g. messing up PRINT would interfere with the code for the interactive console.).
The kinds of questions I am more focused on are things like "what is the operation for NOT on a variable that holds a WORD! like YES or NO." The baseline NOT operation negates the nullity of things... so anything that wasn't null becomes null, and a null becomes undefined. So you can't say not 'true
and get false
. Instead you have to write something like boolean not true? var
(where BOOLEAN constructs TRUE or FALSE as appropriate by detecting a value's nullability).
But while BOOLEAN seems like a solid name for something that does that, I don't know what to call the version for YES and NO or ON and OFF. So right now I'm using placeholders like to-yes-or-no not yes? var
.
Some of these ergonomic issues are the current thorn in the method, which make it a bit more of an ordeal than the comparatively minor change from if toggle
to if on? toggle
Your approach to migration, using customized alerts to catch and prompt fixes for usage of IF on symbolic words, is a smart way to facilitate the transition. It allows for gradual adaptation while moving towards the long-term goal of more flexible usage.
The modularization strategy you described is powerful. Being able to redefine basic functions locally within a module without affecting the wider system provides a great deal of flexibility while maintaining system integrity.
The challenges you're facing with negation operations and type conversion are indeed tricky. The baseline NOT operation's behavior (negating nullity) is consistent with your system's philosophy, but it does create some ergonomic challenges when dealing with symbolic logical values.
Your current approach using
boolean not true? var
is a workable solution, but I can see why you're looking for more elegant alternatives, especially for YES/NO and ON/OFF domains. Some possible ideas:
- For negation:
negate-yesno
orflip-yesno
for YES/NO domaintoggle
orflip-switch
for ON/OFF domain- For type conversion:
to-yesno
(keeping your current naming convention)to-switch
orto-toggle
for ON/OFF- You might consider a more generic approach that allows specifying the domain:
logical-not domain value
where domain could be 'boolean, 'yesno, or 'switchto-logical domain value
for conversionThese are just initial thoughts and might not fully solve the ergonomic issues you're facing.
The transition from
if toggle
toif on? toggle
is indeed relatively minor, but as you pointed out, the more complex operations present larger challenges. It might be worth considering if there's a way to make these operations more consistent across different logical domains, or if the added verbosity is a necessary trade-off for the increased clarity and flexibility of your system.Have you considered creating a set of higher-order functions that could generate domain-specific operations? For example, a function that takes a domain specification and returns appropriate negation and conversion functions for that domain. This could potentially provide a more systematic way of handling these operations across different logical domains.
Your system is pushing the boundaries of how we think about logic in programming languages, and it's natural that you're encountering these kinds of challenges. The solutions you develop could potentially influence future language design in interesting ways.