I open this topic to discuss about the code generalizability (use-cases applicability), practicality (efficiency in code length reduction), consistency (caveats), and clarity (readability) of a novel conditional combination method, namely the “linked booleans logics”.
Story : PEP 505 proposes new operators (based on javascript syntax) to ease the ‘coalescing’ of boolean assessment of None
objects, by implementing the “Safe or”, “Safe getitem”, “Safe getattr” with the following operators :
a ?? b # "Safe or" : a if a is not None else b
a ?[ b ] # "Safe getitem" : a[b] if a is not None and a[b] is not None else None
a ?. b # "Safe getattr" : a.b if a is not None and a.b is not None else None
This syntax is controversed because of the decreased readability of the code due to the introduction of numerous special characters ?
. Additionally, no consensus converges to determine if the use-cases covered by this operators is worth their introduction in the language.
–
The linked boolean logics appears from generalizing the aforementionned operations to equivalent operations on an extended form of boolean, which keep a track of a referenced object (thus “linked” boolean).
The interest does not resides in instanciating the linked booleans but in returning the result of their combination through the linked boolean logics operators.
Thus defining an encasing (let say op{...}
) within which the linked boolean operations would be performed as bitwise operators |
, &
, ^
, getitem and getattr as a[b]
, a.b
would provide the following possible syntax :
op{ a | b } # Safe or
op{ a[b] } # Safe getitem
op{ a.b } # Safe getattr
(note that in order for this to work right now, a
and b
should be linked boolean instances, overriding boolean or, getitem and gettattr with the linked boolean equivalents)
Other functionalities would additionally be provided :
op{ a & b } # "Safe and" (a,b) if (a is not None and b is not None) else None
op{ a ^ b } # "Safe xor" a if (a is not None and b None) else b if (a is None and b is not None) else None
One convenient derivative (e.g. in match
statement) could also be to return the index of the only element assessing True
among a list (like a “mutually exclusive (mutex)” operator) :
op.mutex_idx{ a, b, c} # mutex index
#0 if (a is not None and b is None and c is None) else 1 if (a is None and b is not None and c is None) else 2 if (a is None and b is None and c is not None) else None
–
In the above examples we assumed the default ‘linked boolean function’ is a None
-check (lambda x : x is not None
). Yet, the linked boolean logic can be extended to a wider domain of use-cases by considering other ‘linked boolean functions’.
Consider, for example, an alternative function which returns False for None
or an empty list:
f_el = lambda x: x not in [None, []]
We might override the default ‘linked boolean function’ for the whole operation combinations :
op(f=f_el){ a | b & c }
We might also want to apply the function to an individual linked booleans (thus allowing multiple functions on different elements), for example as :
op{ a | f_el >> b & c }
One last possibly useful thing would be to filter every elements assessing False
from an inputted list, (a syntax should be defined for this).
Additional notes :
Reminder on the precedence order of operators (algebraic operator first, then bitwise ones, then or
and and
then comparative ones). This, as well as the “short-circuiting” behavior of and
, makes difficult the usage of the operator and
in a consistent implementation right now.
A prototype code implementing and testing the linked boolean operators is available here :
“LinkedBool_1 - Pastebin.com”. (EDIT : misuse of “xor” for ‘mutex’ within the prototype, not corrected)