In this post we introduce a new type for “Truth” to the JVM in the Vital Development Kit (VDK), to include cases that don’t fit into Boolean True and False — in particular a value for Unknown and Mu (nonexistence). We use this new Truth type in the VDK for logical and conditional expressions, especially in rules and inference.

Computer hardware has binary — ones and zeros.

Computer software has boolean — true and false.

This seems a perfect match, immutable, a Platonic Ideal.

But there are some wrinkles.

Programming languages deeply use booleans to control the flow of a program:

if this is true, then do that if this is false, then do something else

What if the software doesn’t know a particular value at that moment?

What if the value doesn’t make sense in the current context?

For instance, in the code:

if(TrafficLight.color == RED) then { Stop() } else { Go() }

What if the TrafficLight color is unknown? The software would drive through the traffic intersection. (Hope for the best!)

Or, what if we had code like:

if(Penguin.flightspeed < 10) then { ThrowAFish() }

Should this code work, even if penguins don’t have a flight speed?

In programming languages like Java, these problems lead to a lot of custom workarounds and error checking, so much so that you can no longer use “normal” boolean expressions without a bunch of checks to ensure that it is “safe”.

In rule-based systems or logic languages, often “unknown” is treated as False — this is because a rule like isTall(John) tries to prove that John is tall, and if it can’t it returns “false” or “no”, meaning “I can not prove that”.

But, if the code re-uses that result like:

isShort(X) := NOT isTall(X)

then it is incorrectly combining the Is-Tall case with the I-Don’t-Know case, causing software errors — that is, if the height is unknown, then the person is short, which is quite a leap of logic to be sure.

Three Value Logic

This is not a new problem. SQL tries to solve this with a third logic value called NULL to mean “UNKNOWN”. A description of this is here: https://en.wikipedia.org/wiki/Null_(SQL)

Three value logic (3VL) for True/False/Unknown is well established, with a full description here: https://en.wikipedia.org/wiki/Three-valued_logic

Unfortunately 3VL is not built into languages like Java. And, it gets worse.

Java has low level base types that are efficient like int for integers and object-oriented classes like Integer which have some additional Object overhead but are more “friendly”, and there are ways to convert between Objects and base types. So far, so good.

All objects (instances of classes) may be set to the value of “null”. There is a class for Boolean which may be set to True, False, or Null. And, there is the low-level type “boolean” which may only have the values of true and false. So a Boolean object has a value (“null”) which can not be set in the base type boolean. It is like having a base type for integer that you can’t set to zero.

(Mis)Using Null as a Value

In Java, “null” means uninitialized and is not a true distinct “value”. Some code uses (abuses) “null” to mean “unknown”, but this means we then can’t tell apart an uninitialized value from a initialized “unknown” value — similar to confusing “I-can’t-prove-it” with “false”. Moreover, we can’t store “null” in the low level boolean type anyway, which again can only be true or false.

The value of “null” also occurs in lots of error situations — the network connection failed, the database connection timed out, the password is incorrect, no memory is available, and on and on.

Using “null” as a value reminds me of this Simpon’s line with Homer interpreting the absence of a message (“null”) as a message.

So, the addition of “null” to Java’s Boolean doesn’t provide a way to represent “unknown” unambiguously, and it causes even more confusion with uninitialized objects and various error conditions.

A Value for Nonexistence

Besides the “unknown” case there is also the case above of: Penguin.flightspeed < 10.

Since we know that a penguin can’t have a flightspeed, this isn’t a case of “unknown.” We could argue that this statement of less than 10 is “true” since “0” is the absence of a speed and 0 is less than 10, but that requires embedding domain knowledge about how speeds work, i.e. Is the temperature of a song absolute zero? (-459.67F) Is the color of an integer black? There isn’t a universal way to assign these True or False when the question doesn’t make sense.

And so, we need a fourth value to handle the case of “nonexistence” or “non-applicable”.

We’ve chosen to use the symbol Mu for this as it has been popularly been used in this sense. One notable example of its use is in Douglas R. Hofstadter’s wonderful book:

Gödel, Escher, Bach: An Eternal Golden Braid. (http://www.amazon.com/G%C3%B6del-Escher-Bach-Eternal-Golden/dp/0465026567)

Some more details regarding Mu are in its wikipedia article here: https://en.wikipedia.org/wiki/Mu_(negative)

Truth implementation in the VDK

In the VDK, we use the Groovy language for scripting on the JVM. Truth was added as a new type to Groovy with the values: YES, NO, UNKNOWN, and MU. We use YES and NO instead of True and False to avoid confusion with the existing Boolean values and reserved words.

We follow the truth tables as specified here for 3VL: https://en.wikipedia.org/wiki/Three-valued_logic#Kleene_and_Priest_logics

Some example logic statements using AND, OR, and NOT with Truth:

YES AND UNKNOWN := UNKNOWN

YES OR UNKNOWN := YES

NO OR UNKNOWN := UNKNOWN

NOT UNKNOWN := UNKNOWN

For Mu, any logic expression with Mu yields MU:

NOT MU := MU

YES OR MU := MU

Using our new Truth definition, we can now write or generate code that handles the cases of Unknown or Mu without any ambiguity.

For example:

Truth isTall(Person p) { if( p.height == UNKNOWN ) { return UNKNOWN } if ( p.height > 72.0) { return YES } else { return NO } } Truth isShort(Person p) { if( p.height == UNKNOWN ) { return UNKNOWN } if ( p.height < 60.0) { return YES } else { return NO } } Truth averageHeight(Person p) { return ! ( isTall(p) || isShort(p) ) }

In the above code, the “averageHeight()” function returns a Truth value, and is completely defined by boolean NOT and the isShort() and isTall() functions, and it returns UNKNOWN if either of these functions is UNKNOWN. The NOT (!) works properly now instead of the previous definition which mistakenly combined the “I can’t prove it” case with the “false” case.

Instead of a “if..then” statement in code, we can use a “switch” statement to handle YES, NO, UNKNOWN, and MU, like so:

switch(averageHeight(p) || isShort(p) ) { case YES: println "YES" ; break; case NO: println "NO" ; break; case UNKNOWN: println "UNKNOWN" ; break; case MU: println "MU" ; break; }

We’d like to add a little DSL syntactic sugar to the switch statement, so the above could be written a bit more succinctly, something along the lines of:

/* idea for improvement to Truth DSL */ if (averageHeight(p) || isShort(p) ) { YES: { println "YES" } NO: {println "NO" } UNKNOWN: { println "UNKNOWN" } MU: { println "MU" } }

which would try to follow the pattern of if()…then()…else() but be: if…yes()..no()…unknown()…mu() with any of the blocks optional.

Please comment if you like this proposed addition to the DSL, or have any suggestions!

Feedback

I hope you have enjoyed learning about how Truth is implemented in the VDK to provide a richer logic representation than the Boolean True/False.

Please post any comments or questions in the comment section, or contact Vital AI directly at info@vital.ai