Ternary logic

From Rosetta Code
Task
Ternary logic
You are encouraged to solve this task according to the task description, using any language you may know.
This page uses content from Wikipedia. The original article was at Ternary logic. The list of authors can be seen in the page history. As with Rosetta Code, the text of Wikipedia is available under the GNU FDL. (See links for details on variance)


In logic, a three-valued logic (also trivalent, ternary, or trinary logic, sometimes abbreviated 3VL) is any of several many-valued logic systems in which there are three truth values indicating true, false and some indeterminate third value.

This is contrasted with the more commonly known bivalent logics (such as classical sentential or boolean logic) which provide only for true and false.

Conceptual form and basic ideas were initially created by Łukasiewicz, Lewis and Sulski.

These were then re-formulated by Grigore Moisil in an axiomatic algebraic form, and also extended to n-valued logics in 1945.

Example Ternary Logic Operators in Truth Tables:
not a
¬
True False
Maybe Maybe
False True
a and b
True Maybe False
True True Maybe False
Maybe Maybe Maybe False
False False False False
a or b
True Maybe False
True True True True
Maybe True Maybe Maybe
False True Maybe False
if a then b
True Maybe False
True True Maybe False
Maybe True Maybe Maybe
False True True True
a is equivalent to b
True Maybe False
True True Maybe False
Maybe Maybe Maybe Maybe
False False Maybe True


Task
  • Define a new type that emulates ternary logic by storing data trits.
  • Given all the binary logic operators of the original programming language, reimplement these operators for the new Ternary logic type trit.
  • Generate a sampling of results using trit variables.
  • Kudos for actually thinking up a test case algorithm where ternary logic is intrinsically useful, optimises the test case algorithm and is preferable to binary logic.


Note:   Setun   (Сетунь) was a   balanced ternary   computer developed in 1958 at   Moscow State University.   The device was built under the lead of   Sergei Sobolev   and   Nikolay Brusentsov.   It was the only modern   ternary computer,   using three-valued ternary logic

Action!

DEFINE TERNARY="BYTE"
DEFINE FALSE="0"
DEFINE MAYBE="1"
DEFINE TRUE="2"

PROC PrintT(TERNARY a)
  IF a=FALSE THEN Print("F")
  ELSEIF a=MAYBE THEN Print("?")
  ELSE Print("T")
  FI
RETURN

TERNARY FUNC NotT(TERNARY a)
RETURN (TRUE-a)

TERNARY FUNC AndT(TERNARY a,b)
  IF a<b THEN RETURN (a) FI
RETURN (b)

TERNARY FUNC OrT(TERNARY a,b)
  IF a>b THEN RETURN (a) FI
RETURN (b)

TERNARY FUNC IfThenT(TERNARY a,b)
  IF a=TRUE THEN RETURN (b)
  ELSEIF a=FALSE THEN RETURN (TRUE)
  ELSEIF a+b>TRUE THEN RETURN (TRUE)
  FI
RETURN (MAYBE)

TERNARY FUNC EquivT(TERNARY a,b)
  IF a=b THEN RETURN (TRUE)
  ELSEIF a=TRUE THEN RETURN (b)
  ELSEIF b=TRUE THEN RETURN (a)
  FI
RETURN (MAYBE)

PROC Main()
  BYTE x,y,a,b,res

  x=2 y=1
  FOR a=FALSE TO TRUE
  DO
    res=NotT(a)
    Position(x,y) y==+1
    Print("not ") PrintT(a)
    Print(" = ") PrintT(res)
  OD

  y==+1
  FOR a=FALSE TO TRUE
  DO
    FOR b=FALSE TO TRUE
    DO
      res=AndT(a,b)
      Position(x,y) y==+1
      PrintT(a) Print(" and ") PrintT(b)
      Print(" = ") PrintT(res)
    OD
  OD

  y==+1
  FOR a=FALSE TO TRUE
  DO
    FOR b=FALSE TO TRUE
    DO
      res=OrT(a,b)
      Position(x,y) y==+1
      PrintT(a) Print(" or ") PrintT(b)
      Print(" = ") PrintT(res)
    OD
  OD

  x=20 y=5
  FOR a=FALSE TO TRUE
  DO
    FOR b=FALSE TO TRUE
    DO
      res=IfThenT(a,b)
      Position(x,y) y==+1
      Print("if ") PrintT(a)
      Print(" then ") PrintT(b)
      Print(" = ") PrintT(res)
    OD
  OD

  y==+1
  FOR a=FALSE TO TRUE
  DO
    FOR b=FALSE TO TRUE
    DO
      res=EquivT(a,b)
      Position(x,y) y==+1
      PrintT(a) Print(" equiv ") PrintT(b)
      Print(" = ") PrintT(res)
    OD
  OD
RETURN
Output:

Screenshot from Atari 8-bit computer

not F = T
not ? = ?
not T = F

F and F = F       if F then F = T
F and ? = F       if F then ? = T
F and T = F       if F then T = T
? and F = F       if ? then F = ?
? and ? = ?       if ? then ? = ?
? and T = ?       if ? then T = T
T and F = F       if T then F = F
T and ? = ?       if T then ? = ?
T and T = T       if T then T = T

F or F = F        F equiv F = T
F or ? = ?        F equiv ? = ?
F or T = T        F equiv T = F
? or F = ?        ? equiv F = ?
? or ? = ?        ? equiv ? = T
? or T = T        ? equiv T = ?
T or F = T        T equiv F = F
T or ? = T        T equiv ? = ?
T or T = T        T equiv T = T

Ada

We first specify a package "Logic" for three-valued logic. Observe that predefined Boolean functions, "and" "or" and "not" are overloaded:

package Logic is
   type Ternary is (True, Unknown, False); 

   -- logic functions
   function "and"(Left, Right: Ternary) return Ternary;
   function "or"(Left, Right: Ternary) return Ternary;
   function "not"(T: Ternary) return Ternary;
   function Equivalent(Left, Right: Ternary) return Ternary;
   function Implies(Condition, Conclusion: Ternary) return Ternary;

   -- conversion functions
   function To_Bool(X: Ternary) return Boolean;
   function To_Ternary(B: Boolean) return Ternary;
   function Image(Value: Ternary) return Character;
end Logic;

Next, the implementation of the package:

package body Logic is
   -- type Ternary is (True, Unknown, False);

   function Image(Value: Ternary) return Character is
   begin
      case Value is
         when True    => return 'T';
         when False   => return 'F';
         when Unknown => return '?';
      end case;
   end Image;

   function "and"(Left, Right: Ternary) return Ternary is
   begin
      return Ternary'max(Left, Right);
   end "and";

   function "or"(Left, Right: Ternary) return Ternary is
   begin
      return Ternary'min(Left, Right);
   end "or";

   function "not"(T: Ternary) return Ternary is
   begin
      case T is
         when False   => return True;
         when Unknown => return Unknown;
         when True    => return False;
      end case;
   end "not";

   function To_Bool(X: Ternary) return Boolean is
   begin
      case X is
         when True  => return True;
         when False => return False;
         when Unknown => raise Constraint_Error;
      end case;
   end To_Bool;

   function To_Ternary(B: Boolean) return Ternary is
   begin
      if B then
         return True;
      else
         return False;
      end if;
   end To_Ternary;

   function Equivalent(Left, Right: Ternary) return Ternary is
   begin
      return To_Ternary(To_Bool(Left) = To_Bool(Right));
   exception
      when Constraint_Error => return Unknown;
   end Equivalent;

   function Implies(Condition, Conclusion: Ternary) return Ternary is
   begin
      return (not Condition) or Conclusion;
   end Implies;

end Logic;

Finally, a sample program:

with Ada.Text_IO, Logic;

procedure Test_Tri_Logic is

   use Logic;

   type F2 is access function(Left, Right: Ternary) return Ternary;
   type F1 is access function(Trit: Ternary) return Ternary;

   procedure Truth_Table(F: F1; Name: String) is
   begin
      Ada.Text_IO.Put_Line("X | " & Name & "(X)");
      for T in Ternary loop
         Ada.Text_IO.Put_Line(Image(T) & " |  " & Image(F(T)));
      end loop;
   end Truth_Table;

   procedure Truth_Table(F: F2; Name: String) is
   begin
      Ada.Text_IO.New_Line;
      Ada.Text_IO.Put_Line("X | Y | " & Name & "(X,Y)");
      for X in Ternary loop
         for Y in Ternary loop
            Ada.Text_IO.Put_Line(Image(X) & " | " & Image(Y) & " |  " & Image(F(X,Y)));
         end loop;
      end loop;
   end Truth_Table;

begin
   Truth_Table(F => "not"'Access, Name => "Not");
   Truth_Table(F => "and"'Access, Name => "And");
   Truth_Table(F => "or"'Access, Name => "Or");
   Truth_Table(F => Equivalent'Access, Name => "Eq");
   Truth_Table(F => Implies'Access, Name => "Implies");
end Test_Tri_Logic;
Output:
X | Not(X)
T |  F
? |  ?
F |  T

X | Y | And(X,Y)
T | T |  T
T | ? |  ?
T | F |  F
? | T |  ?
? | ? |  ?
? | F |  F
F | T |  F
F | ? |  F
F | F |  F

... (and so on)

ALGOL 68

Works with: ALGOL 68 version Revision 1 - one minor extension to language used - PRAGMA READ, like C's #include directive.
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny.

File: Ternary_logic.a68

# -*- coding: utf-8 -*- #

INT trit width = 1, trit base = 3;
MODE TRIT = STRUCT(BITS trit);
CO FORMAT trit fmt = $c("?","⌈","⌊",#|"~"#)$; CO

# These values treated are as per "Balanced ternary" #
# eg true=1, maybe=0, false=-1 #
TRIT true =INITTRIT 4r1, maybe=INITTRIT 4r0,
     false=INITTRIT 4r2;

# Warning: redefines standard builtins flip & flop #
LONGCHAR flap="?", flip="⌈", flop="⌊";

OP REPR = (TRIT t)LONGCHAR:
  []LONGCHAR(flap, flip, flop)[1+ABS trit OF t];

############################################
# Define some OPerators for coercing MODES #
############################################
OP INITTRIT = (BOOL in)TRIT:
  (in|true|false);

OP INITBOOL = (TRIT in)BOOL:
  (trit OF in=trit OF true|TRUE|:trit OF in=trit OF false|FALSE|
    raise value error(("vague TRIT to BOOL coercion: """, REPR in,""""));~
  );
OP B = (TRIT in)BOOL: INITBOOL in;

# These values treated are as per "Balanced ternary" #
# n.b true=1, maybe=0, false=-1 #
# Warning: BOOL ABS FALSE (0) is not the same as TRIT ABS false (-1) #

OP INITINT = (TRIT t)INT:
  CASE 1+ABS trit OF t
  IN #maybe# 0, #true # 1, #false#-1
  OUT raise value error(("invalid TRIT value",REPR t)); ~
  ESAC;

OP INITTRIT = (INT in)TRIT: (
  TRIT out;
  trit OF out:= trit OF
    CASE 2+in
    IN false, maybe, true
    OUT raise value error(("invalid TRIT value",in)); ~
    ESAC;
  out
);

OP INITTRIT = (BITS b)TRIT:
  (TRIT out; trit OF out:=b; out);

##################################################
# Define the LOGICAL OPerators for the TRIT MODE #
##################################################
MODE LOGICAL = TRIT;
PR READ "Template_operators_logical_mixin.a68" PR

COMMENT
  Kleene logic truth tables:
END COMMENT

OP AND = (TRIT a,b)TRIT: (
  [,]TRIT(
    # ∧  ##  false, maybe, true  #
    #false# (false, false, false),
    #maybe# (false, maybe, maybe),
    #true # (false, maybe, true )
  )[@-1,@-1][INITINT a, INITINT b]
);

OP OR = (TRIT a,b)TRIT: (
  [,]TRIT(
    # ∨  ##  false, maybe, true #
    #false# (false, maybe, true),
    #maybe# (maybe, maybe, true),
    #true # (true,  true,  true)
  )[@-1,@-1][INITINT a, INITINT b]
);

PRIO IMPLIES = 1; # PRIO = 1.9 #
OP IMPLIES = (TRIT a,b)TRIT: (
  [,]TRIT(
    # ⊃   ## false, maybe, true #
    #false# (true,  true,  true),
    #maybe# (maybe, maybe, true),
    #true # (false, maybe, true)
  )[@-1,@-1][INITINT a, INITINT b]
);

PRIO EQV = 1; # PRIO = 1.8 #
OP EQV = (TRIT a,b)TRIT: (
  [,]TRIT(
    # ≡   ## false, maybe, true #
    #false# (true,  maybe, false),
    #maybe# (maybe, maybe, maybe),
    #true # (false, maybe, true )
  )[@-1,@-1][INITINT a, INITINT b]
);
File: Template_operators_logical_mixin.a68
# -*- coding: utf-8 -*- #

OP & = (LOGICAL a,b)LOGICAL: a AND b;
CO # not included as they are treated as SCALAR #
OP EQ = (LOGICAL a,b)LOGICAL: a = b,
   NE = (LOGICAL a,b)LOGICAL: a /= b,
   ≠ = (TRIT a,b)TRIT: a /= b,
   ¬= = (TRIT a,b)TRIT: a /= b;
END CO

#IF html entities possible THEN
¢ "parked" operators for completeness ¢
OP ¬ = (LOGICAL a)LOGICAL: NOT a,
   ∧  = (LOGICAL a,b)LOGICAL: a AND b,
   /\ = (LOGICAL a,b)LOGICAL: a AND b,
   ∨  = (LOGICAL a,b)LOGICAL: a OR b,
   \/ = (LOGICAL a,b)LOGICAL: a OR b,
   ⊃ = (TRIT a,b)TRIT: a IMPLIES b,
   ≡ = (TRIT a,b)TRIT: a EQV b;
FI#

#IF algol68c THEN
OP ~ = (LOGICAL a)LOGICAL: NOT a,
   ~= = (LOGICAL a,b)LOGICAL: a /= b; SCALAR!
FI#
File: test_Ternary_logic.a68
#!/usr/local/bin/a68g --script #
# -*- coding: utf-8 -*- #

PR READ "prelude/general.a68" PR
PR READ "Ternary_logic.a68" PR

[]TRIT trits = (false, maybe, true);

FORMAT 
  col fmt = $" "g" "$,
  row fmt = $l3(f(col fmt)"|")f(col fmt)$,
  row sep fmt = $l3("---+")"---"l$;

PROC row sep = VOID:
  printf(row sep fmt);

PROC title = (STRING op name, LONGCHAR op char)VOID:(
  print(("Operator: ",op name));
  printf((row fmt,op char,REPR false, REPR maybe, REPR true))
);

PROC print trit op table = (LONGCHAR op char, STRING op name, PROC(TRIT,TRIT)TRIT op)VOID: (
  printf($l$);
  title(op name, op char);
  FOR i FROM LWB trits TO UPB trits DO
    row sep;
    TRIT ti = trits[i];
    printf((col fmt, REPR ti));
    FOR j FROM LWB trits TO UPB trits DO
      TRIT tj = trits[j];
      printf(($"|"$, col fmt, REPR op(ti,tj)))
    OD
  OD;
  printf($l$)
);

printf((
  $"Comparitive table of coercions:"l$,
  $"  TRIT BOOL         INT"l$
));

FOR it FROM LWB trits TO UPB trits DO
  TRIT t = trits[it];
  printf(( $"  "g"  "$, REPR t, 
    IF trit OF t = trit OF maybe THEN " " ELSE B t FI,
    INITINT t, $l$))
OD;

printf((
  $l"Specific test of the IMPLIES operator:"l$,
  $"  "g" implies "g" is "b("not ","")"a contradiction!"l$,
    B false,    B false,    B(false IMPLIES false),
    B false,    B true,     B(false IMPLIES true),
    B false,    REPR maybe, B(false IMPLIES maybe),
    B true,     B false,    B(true  IMPLIES false),
    B true,     B true,     B(true  IMPLIES true),
    REPR maybe, Btrue,      B(maybe IMPLIES true),
  $"  "g" implies "g" is "g" a contradiction!"l$,
    B true,     REPR maybe, REPR (true  IMPLIES maybe),
    REPR maybe, B false,    REPR (maybe IMPLIES false),
    REPR maybe, REPR maybe, REPR (maybe IMPLIES maybe),
  $l$
));

printf($"Kleene logic truth table samples:"l$);

print trit op table("≡","EQV",     (TRIT a,b)TRIT: a EQV b);
print trit op table("⊃","IMPLIES", (TRIT a,b)TRIT: a IMPLIES b);
print trit op table("∧","AND",     (TRIT a,b)TRIT: a AND b);
print trit op table("∨","OR",      (TRIT a,b)TRIT: a OR b)
Output:
Comparitive table of coercions:
  TRIT BOOL         INT
  ⌊    F             -1  
  ?                  +0  
  ⌈    T             +1  

Specific test of the IMPLIES operator:
  F implies F is not a contradiction!
  F implies T is not a contradiction!
  F implies ? is not a contradiction!
  T implies F is a contradiction!
  T implies T is not a contradiction!
  ? implies T is not a contradiction!
  T implies ? is ? a contradiction!
  ? implies F is ? a contradiction!
  ? implies ? is ? a contradiction!

Kleene logic truth table samples:

Operator: EQV
 ≡ | ⌊ | ? | ⌈ 
---+---+---+---
 ⌊ | ⌈ | ? | ⌊ 
---+---+---+---
 ? | ? | ? | ? 
---+---+---+---
 ⌈ | ⌊ | ? | ⌈ 

Operator: IMPLIES
 ⊃ | ⌊ | ? | ⌈ 
---+---+---+---
 ⌊ | ⌈ | ⌈ | ⌈ 
---+---+---+---
 ? | ? | ? | ⌈ 
---+---+---+---
 ⌈ | ⌊ | ? | ⌈ 

Operator: AND
 ∧ | ⌊ | ? | ⌈ 
---+---+---+---
 ⌊ | ⌊ | ⌊ | ⌊ 
---+---+---+---
 ? | ⌊ | ? | ? 
---+---+---+---
 ⌈ | ⌊ | ? | ⌈ 

Operator: OR
 ∨ | ⌊ | ? | ⌈ 
---+---+---+---
 ⌊ | ⌊ | ? | ⌈ 
---+---+---+---
 ? | ? | ? | ⌈ 
---+---+---+---
 ⌈ | ⌈ | ⌈ | ⌈ 

Arturo

Arturo's :logical values inherently support ternary logic (true, false and maybe) and the corresponding logical operations.

vals: @[true maybe false]

loop vals 'v -> print ["NOT" v "=>" not? v]
print ""
loop vals 'v1 [
    loop vals 'v2 
        -> print [v1 "AND" v2 "=>" and? v1 v2]
]
print ""
loop vals 'v1 [
    loop vals 'v2 
        -> print [v1 "OR" v2 "=>" or? v1 v2]
]
print ""
loop vals 'v1 [
    loop vals 'v2 
        -> print [v1 "XOR" v2 "=>" xor? v1 v2]
]
Output:
NOT true => false 
NOT maybe => maybe 
NOT false => true 

true AND true => true 
true AND maybe => maybe 
true AND false => false 
maybe AND true => maybe 
maybe AND maybe => maybe 
maybe AND false => false 
false AND true => false 
false AND maybe => false 
false AND false => false 

true OR true => true 
true OR maybe => true 
true OR false => true 
maybe OR true => true 
maybe OR maybe => maybe 
maybe OR false => maybe 
false OR true => true 
false OR maybe => maybe 
false OR false => false 

true XOR true => false 
true XOR maybe => maybe 
true XOR false => true 
maybe XOR true => maybe 
maybe XOR maybe => maybe 
maybe XOR false => maybe 
false XOR true => true 
false XOR maybe => maybe 
false XOR false => false


AutoHotkey

Ternary_Not(a){
	SetFormat, Float, 2.1
	return Abs(a-1)
}

Ternary_And(a,b){
	return a<b?a:b
}

Ternary_Or(a,b){
	return a>b?a:b
}

Ternary_IfThen(a,b){
	return a=1?b:a=0?1:a+b>1?1:0.5
}

Ternary_Equiv(a,b){
	return a=b?1:a=1?b:b=1?a:0.5
}
Examples:
aa:=[1,0.5,0]
bb:=[1,0.5,0]

for index, a in aa
	Res .= "`tTernary_Not`t" a "`t=`t" Ternary_Not(a) "`n"
Res .= "-------------`n"

for index, a in aa
	for index, b in bb
		Res .= a "`tTernary_And`t" b "`t=`t" Ternary_And(a,b) "`n"
Res .= "-------------`n"

for index, a in aa
	for index, b in bb
		Res .= a "`tTernary_or`t" b "`t=`t" Ternary_Or(a,b) "`n"
Res .= "-------------`n"

for index, a in aa
	for index, b in bb
		Res .= a "`tTernary_then`t" b "`t=`t" Ternary_IfThen(a,b) "`n"
Res .= "-------------`n"

for index, a in aa
	for index, b in bb
		Res .= a "`tTernary_equiv`t" b "`t=`t" Ternary_Equiv(a,b) "`n"

StringReplace, Res, Res, 1, true, all
StringReplace, Res, Res, 0.5, maybe, all
StringReplace, Res, Res, 0, false, all
MsgBox % Res
return
Output:
	Ternary_Not	true	=	false
	Ternary_Not	maybe	=	maybe
	Ternary_Not	false	=	true
-------------
true	Ternary_And	true	=	true
true	Ternary_And	maybe	=	maybe
true	Ternary_And	false	=	false
maybe	Ternary_And	true	=	maybe
maybe	Ternary_And	maybe	=	maybe
maybe	Ternary_And	false	=	false
false	Ternary_And	true	=	false
false	Ternary_And	maybe	=	false
false	Ternary_And	false	=	false
-------------
true	Ternary_or	true	=	true
true	Ternary_or	maybe	=	true
true	Ternary_or	false	=	true
maybe	Ternary_or	true	=	true
maybe	Ternary_or	maybe	=	maybe
maybe	Ternary_or	false	=	maybe
false	Ternary_or	true	=	true
false	Ternary_or	maybe	=	maybe
false	Ternary_or	false	=	false
-------------
true	Ternary_then	true	=	true
true	Ternary_then	maybe	=	maybe
true	Ternary_then	false	=	false
maybe	Ternary_then	true	=	true
maybe	Ternary_then	maybe	=	maybe
maybe	Ternary_then	false	=	maybe
false	Ternary_then	true	=	true
false	Ternary_then	maybe	=	true
false	Ternary_then	false	=	true
-------------
true	Ternary_equiv	true	=	true
true	Ternary_equiv	maybe	=	maybe
true	Ternary_equiv	false	=	false
maybe	Ternary_equiv	true	=	maybe
maybe	Ternary_equiv	maybe	=	true
maybe	Ternary_equiv	false	=	maybe
false	Ternary_equiv	true	=	false
false	Ternary_equiv	maybe	=	maybe
false	Ternary_equiv	false	=	true


BASIC256

Translation of: Liberty BASIC
global tFalse, tDontKnow, tTrue
tFalse = 0
tDontKnow = 1
tTrue = 2

print "Nombres cortos y largos para valores lógicos ternarios:"
for i = tFalse to tTrue
	print shortName3$(i); "  "; longName3$(i)
next i
print

print "Funciones de parámetro único"
print "x"; " "; "=x"; "  "; "not(x)"
for i = tFalse to tTrue
	print shortName3$(i); "  "; shortName3$(i); "    "; shortName3$(not3(i))
next i
print

print "Funciones de doble parámetro"
print "x";" ";"y";"  ";"x AND y";"  ";"x OR y";"  ";"x EQ y";"  ";"x XOR y"
for a = tFalse to tTrue
	for b = tFalse to tTrue
		print shortName3$(a); " "; shortName3$(b); "     ";
		print shortName3$(and3(a,b)); "       "; shortName3$(or3(a,b));"       ";
		print shortName3$(eq3(a,b)); "        "; shortName3$(xor3(a,b))
	next b
next a
end

function and3(a,b)
	if a < b then return a else return b
end function

function or3(a,b)
	if a > b then return a else return b
end function

function eq3(a,b)
	begin case
		case a = tDontKnow or b = tDontKnow
			return tDontKnow
		case a = b
			return tTrue
		else
			return tFalse
	end case
end function

function xor3(a,b)
	return not3(eq3(a,b))
end function

function not3(b)
	return 2-b
end function

function shortName3$(i)
	return mid("F?T", i+1, 1)
end function

function longName3$(i)
	begin case
		case i = 1
			return "Don't know"
		case i = 2
			return "True"
		else
			return "False"
	end case
end function
Output:
Igual que la entrada de Liberty BASIC.

BBC BASIC

      INSTALL @lib$ + "CLASSLIB"
      
      REM Create a ternary class:
      DIM trit{tor, tand, teqv, tnot, tnor, s, v}
      DEF PRIVATE trit.s (t&) LOCAL t$():DIM t$(2):t$()="FALSE","MAYBE","TRUE":=t$(t&)
      DEF PRIVATE trit.v (t$) = INSTR("FALSE MAYBE TRUE", t$) DIV 6
      DEF trit.tnot (t$) = FN(trit.s)(2 - FN(trit.v)(t$))
      DEF trit.tor (a$,b$) LOCAL t:t=FN(trit.v)(a$)ORFN(trit.v)(b$):=FN(trit.s)(t+(t>2))
      DEF trit.tnor (a$,b$) = FN(trit.tnot)(FN(trit.tor)(a$,b$))
      DEF trit.tand (a$,b$) = FN(trit.tnor)(FN(trit.tnot)(a$),FN(trit.tnot)(b$))
      DEF trit.teqv (a$,b$) = FN(trit.tor)(FN(trit.tand)(a$,b$),FN(trit.tnor)(a$,b$))
      PROC_class(trit{})
      
      PROC_new(mytrit{}, trit{})
      
      REM Test it:
      PRINT "Testing NOT:"
      PRINT "NOT FALSE = " FN(mytrit.tnot)("FALSE")
      PRINT "NOT MAYBE = " FN(mytrit.tnot)("MAYBE")
      PRINT "NOT TRUE  = " FN(mytrit.tnot)("TRUE")
      
      PRINT '"Testing OR:"
      PRINT "FALSE OR FALSE = " FN(mytrit.tor)("FALSE","FALSE")
      PRINT "FALSE OR MAYBE = " FN(mytrit.tor)("FALSE","MAYBE")
      PRINT "FALSE OR TRUE  = " FN(mytrit.tor)("FALSE","TRUE")
      PRINT "MAYBE OR MAYBE = " FN(mytrit.tor)("MAYBE","MAYBE")
      PRINT "MAYBE OR TRUE  = " FN(mytrit.tor)("MAYBE","TRUE")
      PRINT "TRUE  OR TRUE  = " FN(mytrit.tor)("TRUE","TRUE")
      
      PRINT '"Testing AND:"
      PRINT "FALSE AND FALSE = " FN(mytrit.tand)("FALSE","FALSE")
      PRINT "FALSE AND MAYBE = " FN(mytrit.tand)("FALSE","MAYBE")
      PRINT "FALSE AND TRUE  = " FN(mytrit.tand)("FALSE","TRUE")
      PRINT "MAYBE AND MAYBE = " FN(mytrit.tand)("MAYBE","MAYBE")
      PRINT "MAYBE AND TRUE  = " FN(mytrit.tand)("MAYBE","TRUE")
      PRINT "TRUE  AND TRUE  = " FN(mytrit.tand)("TRUE","TRUE")
      
      PRINT '"Testing EQV (similar to EOR):"
      PRINT "FALSE EQV FALSE = " FN(mytrit.teqv)("FALSE","FALSE")
      PRINT "FALSE EQV MAYBE = " FN(mytrit.teqv)("FALSE","MAYBE")
      PRINT "FALSE EQV TRUE  = " FN(mytrit.teqv)("FALSE","TRUE")
      PRINT "MAYBE EQV MAYBE = " FN(mytrit.teqv)("MAYBE","MAYBE")
      PRINT "MAYBE EQV TRUE  = " FN(mytrit.teqv)("MAYBE","TRUE")
      PRINT "TRUE  EQV TRUE  = " FN(mytrit.teqv)("TRUE","TRUE")
      
      PROC_discard(mytrit{})
Output:
Testing NOT:
NOT FALSE = TRUE
NOT MAYBE = MAYBE
NOT TRUE  = FALSE

Testing OR:
FALSE OR FALSE = FALSE
FALSE OR MAYBE = MAYBE
FALSE OR TRUE  = TRUE
MAYBE OR MAYBE = MAYBE
MAYBE OR TRUE  = TRUE
TRUE  OR TRUE  = TRUE

Testing AND:
FALSE AND FALSE = FALSE
FALSE AND MAYBE = FALSE
FALSE AND TRUE  = FALSE
MAYBE AND MAYBE = MAYBE
MAYBE AND TRUE  = MAYBE
TRUE  AND TRUE  = TRUE

Testing EQV (similar to EOR):
FALSE EQV FALSE = TRUE
FALSE EQV MAYBE = MAYBE
FALSE EQV TRUE  = FALSE
MAYBE EQV MAYBE = MAYBE
MAYBE EQV TRUE  = MAYBE
TRUE  EQV TRUE  = TRUE

Bruijn

Direct translations of the truth tables to lambda calculus. The operators could be golfed significantly. If you do so, please add them here!

For applications of Ternary logic, see bruijn's balanced ternary implementation.

true [[[0]]]

maybe [[[1]]]

false [[[2]]]

¬‣ [0 true maybe false]

…⋀… [[1 (0 1 1 1) (0 0 0 1) (0 0 0 0)]]

…⋁… [[1 (0 0 0 0) (0 1 0 0) (0 1 1 1)]]

…⊃… [[1 (0 true 0 1) (0 true 1 1) (0 1 1 1)]]

…≡… [[1 (0 true 0 1) (0 1 1 1) (0 0 0 0)]]

# --- result samples ---

:import std/List .

main [[inp <> "=" <> !res ++ "\n"] <++> (cross3 ops trits trits)]
	!‣ [0 "false" "maybe" "true"]
	…<>… [[1 ++ " " ++ 0]]
	inp 0 [[~1 <> (0 [[!1 <> (0 [[!1]])]])]]
	res ^(^0) ^(~0) ^(~(~0))
	ops (…⋀… : "and") : ((…⋁… : "or") : ((…⊃… : "if") : {}(…≡… : "equiv")))
	trits true : (maybe : {}false)
Output:
and true true = true
and true maybe = maybe
and true false = false
and maybe true = maybe
and maybe maybe = maybe
and maybe false = false
and false true = false
and false maybe = false
and false false = false
or true true = true
or true maybe = true
or true false = true
or maybe true = true
or maybe maybe = maybe
or maybe false = maybe
or false true = true
or false maybe = maybe
or false false = false
if true true = true
if true maybe = true
if true false = true
if maybe true = maybe
if maybe maybe = maybe
if maybe false = true
if false true = false
if false maybe = maybe
if false false = true
equiv true true = true
equiv true maybe = maybe
equiv true false = false
equiv maybe true = maybe
equiv maybe maybe = maybe
equiv maybe false = maybe
equiv false true = false
equiv false maybe = maybe
equiv false false = true

C

Implementing logic using lookup tables

#include <stdio.h>
 
typedef enum {
  TRITTRUE,  /* In this enum, equivalent to integer value 0 */
  TRITMAYBE, /* In this enum, equivalent to integer value 1 */
  TRITFALSE  /* In this enum, equivalent to integer value 2 */
} trit;
 
/* We can trivially find the result of the operation by passing
   the trinary values as indeces into the lookup tables' arrays. */
trit tritNot[3] = {TRITFALSE , TRITMAYBE, TRITTRUE};
trit tritAnd[3][3] = { {TRITTRUE, TRITMAYBE, TRITFALSE},
                       {TRITMAYBE, TRITMAYBE, TRITFALSE},
                       {TRITFALSE, TRITFALSE, TRITFALSE} };
 
trit tritOr[3][3] = { {TRITTRUE, TRITTRUE, TRITTRUE},
                      {TRITTRUE, TRITMAYBE, TRITMAYBE},
                      {TRITTRUE, TRITMAYBE, TRITFALSE} };
 
trit tritThen[3][3] = { { TRITTRUE, TRITMAYBE, TRITFALSE},
                        { TRITTRUE, TRITMAYBE, TRITMAYBE},
                        { TRITTRUE, TRITTRUE, TRITTRUE } };
 
trit tritEquiv[3][3] = { { TRITTRUE, TRITMAYBE, TRITFALSE},
                         { TRITMAYBE, TRITMAYBE, TRITMAYBE},
                         { TRITFALSE, TRITMAYBE, TRITTRUE } };

/* Everything beyond here is just demonstration */

const char* tritString[3] = {"T", "?", "F"};

void demo_binary_op(trit operator[3][3], const char* name)
{
  trit operand1 = TRITTRUE; /* Declare. Initialize for CYA */
  trit operand2 = TRITTRUE; /* Declare. Initialize for CYA */

  /* Blank line */
  printf("\n");

  /* Demo this operator */
  for( operand1 = TRITTRUE; operand1 <= TRITFALSE; ++operand1 )
  {
    for( operand2 = TRITTRUE; operand2 <= TRITFALSE; ++operand2 )
    {
      printf("%s %s %s: %s\n", tritString[operand1],
                               name,
                               tritString[operand2],
                               tritString[operator[operand1][operand2]]);
    }
  }

}

int main()
{
  trit op1 = TRITTRUE; /* Declare. Initialize for CYA */
  trit op2 = TRITTRUE; /* Declare. Initialize for CYA */
 
  /* Demo 'not' */
  for( op1 = TRITTRUE; op1 <= TRITFALSE; ++op1 )
  {
    printf("Not %s: %s\n", tritString[op1], tritString[tritNot[op1]]);
  }
  demo_binary_op(tritAnd, "And");
  demo_binary_op(tritOr, "Or");
  demo_binary_op(tritThen, "Then");
  demo_binary_op(tritEquiv, "Equiv");

 
  return 0;
}
Output:
Not T: F
Not ?: ?
Not F: T

T And T: T
T And ?: ?
T And F: F
? And T: ?
? And ?: ?
? And F: F
F And T: F
F And ?: F
F And F: F

T Or T: T
T Or ?: T
T Or F: T
? Or T: T
? Or ?: ?
? Or F: ?
F Or T: T
F Or ?: ?
F Or F: F

T Then T: T
T Then ?: ?
T Then F: F
? Then T: T
? Then ?: ?
? Then F: ?
F Then T: T
F Then ?: T
F Then F: T

T Equiv T: T
T Equiv ?: ?
T Equiv F: F
? Equiv T: ?
? Equiv ?: ?
? Equiv F: ?
F Equiv T: F
F Equiv ?: ?
F Equiv F: T

Using functions

#include <stdio.h>

typedef enum { t_F = -1, t_M, t_T } trit;

trit t_not  (trit a) { return -a; }
trit t_and  (trit a, trit b) { return a < b ? a : b; }
trit t_or   (trit a, trit b) { return a > b ? a : b; }
trit t_eq   (trit a, trit b) { return a * b; }
trit t_imply(trit a, trit b) { return -a > b ? -a : b; }
char t_s(trit a) { return "F?T"[a + 1]; }

#define forall(a) for(a = t_F; a <= t_T; a++)
void show_op(trit (*f)(trit, trit), const char *name) {
	trit a, b;
	printf("\n[%s]\n    F ? T\n  -------", name);
	forall(a) {
		printf("\n%c |", t_s(a));
		forall(b) printf(" %c", t_s(f(a, b)));
	}
	puts("");
}

int main(void)
{
	trit a;

	puts("[Not]");
	forall(a) printf("%c | %c\n", t_s(a), t_s(t_not(a)));

	show_op(t_and,   "And");
	show_op(t_or,    "Or");
	show_op(t_eq,    "Equiv");
	show_op(t_imply, "Imply");

	return 0;
}
Output:
[Not]
F | T
? | ?
T | F

[And]
    F ? T
  -------
F | F F F
? | F ? ?
T | F ? T

[Or]
    F ? T
  -------
F | F ? T
? | ? ? T
T | T T T

[Equiv]
    F ? T
  -------
F | T ? F
? | ? ? ?
T | F ? T

[Imply]
    F ? T
  -------
F | T T T
? | ? ? T
T | F ? T

Variable truthfulness

Represent each possible truth value as a floating point value x, where the var has x chance of being true and 1 - x chance of being false. When using if3 conditional on a potential truth varible, the result is randomly sampled to true or false according to the chance. (This description is definitely very confusing perhaps).

#include <stdio.h>
#include <stdlib.h>

typedef double half_truth, maybe;

inline maybe not3(maybe a) { return 1 - a; }

inline maybe
and3(maybe a, maybe b) { return a * b; }

inline maybe
or3(maybe a, maybe b) { return a + b - a * b; }

inline maybe
eq3(maybe a, maybe b) { return 1 - a - b + 2 * a * b; }

inline maybe
imply3(maybe a, maybe b) { return or3(not3(a), b); }

#define true3(x) ((x) * RAND_MAX > rand())
#define if3(x) if (true3(x))

int main()
{
	maybe roses_are_red = 0.25; /* they can be white or black, too */
	maybe violets_are_blue = 1; /* aren't they just */
	int i;

	puts("Verifying flowery truth for 40 times:\n");

	puts("Rose is NOT red:"); /* chance: .75 */
	for (i = 0; i < 40 || !puts("\n"); i++)
		printf( true3( not3(roses_are_red) ) ? "T" : "_");

	/* pick a rose and a violet; */
	puts("Rose is red AND violet is blue:");
	/* chance of rose being red AND violet being blue is .25 */
	for (i = 0; i < 40 || !puts("\n"); i++)
		printf( true3( and3(roses_are_red, violets_are_blue) )
			? "T" : "_");

	/* chance of rose being red OR violet being blue is 1 */
	puts("Rose is red OR violet is blue:");
	for (i = 0; i < 40 || !puts("\n"); i++)
		printf( true3( or3(roses_are_red, violets_are_blue) )
			? "T" : "_");

	/* pick two roses; chance of em being both red or both not red is .625 */
	puts("This rose is as red as that rose:");
	for (i = 0; i < 40 || !puts("\n"); i++)
		if3(eq3(roses_are_red, roses_are_red)) putchar('T');
		else putchar('_');

	return 0;
}

C#

using System;

/// <summary>
/// Extension methods on nullable bool.
/// </summary>
/// <remarks>
/// The operators !, & and | are predefined.
/// </remarks>
public static class NullableBoolExtension
{
    public static bool? Implies(this bool? left, bool? right)
    {
        return !left | right;
    }

    public static bool? IsEquivalentTo(this bool? left, bool? right)
    {
        return left.HasValue && right.HasValue ? left == right : default(bool?);
    }

    public static string Format(this bool? value)
    {
        return value.HasValue ? value.Value.ToString() : "Maybe";
    }
}

public class Program
{
    private static void Main()
    {
        var values = new[] { true, default(bool?), false };

        foreach (var left in values)
        {
            Console.WriteLine("¬{0} = {1}", left.Format(), (!left).Format());
            foreach (var right in values)
            {
                Console.WriteLine("{0} & {1} = {2}", left.Format(), right.Format(), (left & right).Format());
                Console.WriteLine("{0} | {1} = {2}", left.Format(), right.Format(), (left | right).Format());
                Console.WriteLine("{0} → {1} = {2}", left.Format(), right.Format(), left.Implies(right).Format());
                Console.WriteLine("{0} ≡ {1} = {2}", left.Format(), right.Format(), left.IsEquivalentTo(right).Format());
            }
        }
    }
}
Output:
¬True = False
True & True = True
True | True = True
True → True = True
True ≡ True = True
True & Maybe = Maybe
True | Maybe = True
True → Maybe = Maybe
True ≡ Maybe = Maybe
True & False = False
True | False = True
True → False = False
True ≡ False = False
¬Maybe = Maybe
Maybe & True = Maybe
Maybe | True = True
Maybe → True = True
Maybe ≡ True = Maybe
Maybe & Maybe = Maybe
Maybe | Maybe = Maybe
Maybe → Maybe = Maybe
Maybe ≡ Maybe = Maybe
Maybe & False = False
Maybe | False = Maybe
Maybe → False = Maybe
Maybe ≡ False = Maybe
¬False = True
False & True = False
False | True = True
False → True = True
False ≡ True = False
False & Maybe = False
False | Maybe = Maybe
False → Maybe = True
False ≡ Maybe = Maybe
False & False = False
False | False = False
False → False = True
False ≡ False = True

C++

Essentially the same logic as the Using functions implementation above, but using class-based encapsulation and overridden operators.

#include <iostream>
#include <stdlib.h>

class trit {
public:
    static const trit False, Maybe, True;

    trit operator !() const {
        return static_cast<Value>(-value);
    }

    trit operator &&(const trit &b) const {
        return (value < b.value) ? value : b.value;
    }

    trit operator ||(const trit &b) const {
        return (value > b.value) ? value : b.value;
    }

    trit operator >>(const trit &b) const {
        return -value > b.value ? static_cast<Value>(-value) : b.value;
    }

    trit operator ==(const trit &b) const {
        return static_cast<Value>(value * b.value);
    }

    char chr() const {
        return "F?T"[value + 1];
    }

protected:
    typedef enum { FALSE=-1, MAYBE, TRUE } Value;

    Value value;

    trit(const Value value) : value(value) { }
};

std::ostream& operator<<(std::ostream &os, const trit &t)
{
    os << t.chr();
    return os;
}

const trit trit::False = trit(trit::FALSE);
const trit trit::Maybe = trit(trit::MAYBE);
const trit trit::True = trit(trit::TRUE);

int main(int, char**) {
    const trit trits[3] = { trit::True, trit::Maybe, trit::False };

#define for_each(name) \
    for (size_t name=0; name<3; ++name)

#define show_op(op) \
    std::cout << std::endl << #op << " "; \
    for_each(a) std::cout << ' ' << trits[a]; \
    std::cout << std::endl << "  -------"; \
    for_each(a) { \
        std::cout << std::endl << trits[a] << " |"; \
        for_each(b) std::cout << ' ' << (trits[a] op trits[b]); \
    } \
    std::cout << std::endl;

    std::cout << "! ----" << std::endl;
    for_each(a) std::cout << trits[a] << " | " << !trits[a] << std::endl;

    show_op(&&);
    show_op(||);
    show_op(>>);
    show_op(==);
    return EXIT_SUCCESS;
}
Output:
! ----
T | F
? | ?
F | T

&&  T ? F
  -------
T | T ? F
? | ? ? F
F | F F F

||  T ? F
  -------
T | T T T
? | T ? ?
F | T ? F

>>  T ? F
  -------
T | T ? F
? | T ? ?
F | T T T

==  T ? F
  -------
T | T ? F
? | ? ? ?
F | F ? T

Common Lisp

(defun tri-not (x) (- 1 x))
(defun tri-and (&rest x) (apply #'* x))
(defun tri-or (&rest x) (tri-not (apply #'* (mapcar #'tri-not x))))
(defun tri-eq (x y) (+ (tri-and x y) (tri-and (- 1 x) (- 1 y))))
(defun tri-imply (x y) (tri-or (tri-not x) y))

(defun tri-test (x) (< (random 1e0) x))
(defun tri-string (x) (if (= x 1) "T" (if (= x 0) "F" "?")))

;; to say (tri-if (condition) (yes) (no))
(defmacro tri-if (tri ifcase &optional elsecase)
  `(if (tri-test ,tri) ,ifcase ,elsecase))

(defun print-table (func header)
  (let ((vals '(1 .5 0)))
    (format t "~%~a:~%" header)
    (format t "    ~{~a ~^~}~%---------~%" (mapcar #'tri-string vals))
    (loop for row in vals do
	  (format t "~a | " (tri-string row))
	  (loop for col in vals do
		(format t "~a " (tri-string (funcall func row col))))
	  (write-line ""))))

(write-line "NOT:")
(loop for row in '(1 .5 0) do
      (format t "~a | ~a~%" (tri-string row) (tri-string (tri-not row))))

(print-table #'tri-and   "AND")
(print-table #'tri-or    "OR")
(print-table #'tri-imply "IMPLY")
(print-table #'tri-eq    "EQUAL")
Output:
NOT:
T | F
? | ?
F | T

AND:
    T ? F 
---------
T | T ? F 
? | ? ? F 
F | F F F 

OR:
    T ? F 
---------
T | T T T 
? | T ? ? 
F | T ? F 

IMPLY:
    T ? F 
---------
T | T ? F 
? | T ? ? 
F | T T T 

EQUAL:
    T ? F 
---------
T | T ? F 
? | ? ? ? 
F | F ? T

D

Partial translation of a C entry:

import std.stdio;

struct Trit {
    private enum Val : byte { F = -1, M, T }
    private Val t;
    alias t this;
    static immutable Trit[3] vals = [{Val.F}, {Val.M}, {Val.T}];
    static immutable F = Trit(Val.F); // Not necessary but handy.
    static immutable M = Trit(Val.M);
    static immutable T = Trit(Val.T);

    string toString() const pure nothrow {
        return "F?T"[t + 1  .. t + 2];
    }

    Trit opUnary(string op)() const pure nothrow
    if (op == "~") {
        return Trit(-t);
    }

    Trit opBinary(string op)(in Trit b) const pure nothrow
    if (op == "&") {
        return t < b ? this : b;
    }

    Trit opBinary(string op)(in Trit b) const pure nothrow
    if (op == "|") {
        return t > b ? this : b;
    }

    Trit opBinary(string op)(in Trit b) const pure nothrow
    if (op == "^") {
        return ~(this == b);
    }

    Trit opEquals(in Trit b) const pure nothrow {
        return Trit(cast(Val)(t * b));
    }

    Trit imply(in Trit b) const pure nothrow {
        return -t > b ? ~this : b;
    }
}

void showOperation(string op)(in string opName) {
    writef("\n[%s]\n    F ? T\n  -------", opName);
    foreach (immutable a; Trit.vals) {
        writef("\n%s |", a);
        foreach (immutable b; Trit.vals)
            static if (op == "==>")
                writef(" %s", a.imply(b));
            else
                writef(" %s", mixin("a " ~ op ~ " b"));
    }
    writeln();
}

void main() {
    writeln("[Not]");
    foreach (const a; Trit.vals)
        writefln("%s | %s", a, ~a);

    showOperation!"&"("And");
    showOperation!"|"("Or");
    showOperation!"^"("Xor");
    showOperation!"=="("Equiv");
    showOperation!"==>"("Imply");
}
Output:
[Not]
F | T
? | ?
T | F

[And]
    F ? T
  -------
F | F F F
? | F ? ?
T | F ? T

[Or]
    F ? T
  -------
F | F ? T
? | ? ? T
T | T T T

[Xor]
    F ? T
  -------
F | F ? T
? | ? ? ?
T | T ? F

[Equiv]
    F ? T
  -------
F | T ? F
? | ? ? ?
T | F ? T

[Imply]
    F ? T
  -------
F | T T T
? | ? ? T
T | F ? T

Delphi

unit TrinaryLogic;

interface

//Define our own type for ternary logic.
//This is actually still a Boolean, but the compiler will use distinct RTTI information.
type
    TriBool = type Boolean;

const
    TTrue:TriBool = True;
    TFalse:TriBool = False;
    TMaybe:TriBool = TriBool(2);

function TVL_not(Value: TriBool): TriBool;
function TVL_and(A, B: TriBool): TriBool;
function TVL_or(A, B: TriBool): TriBool;
function TVL_xor(A, B: TriBool): TriBool;
function TVL_eq(A, B: TriBool): TriBool;

implementation

Uses
    SysUtils;

function TVL_not(Value: TriBool): TriBool;
begin
    if Value = True Then
        Result := TFalse
    else If Value = False Then
        Result := TTrue
    else
        Result := Value;
end;

function TVL_and(A, B: TriBool): TriBool;
begin
    Result := TriBool(Iff(Integer(A * B) > 1, Integer(TMaybe), A * B));
end;

function TVL_or(A, B: TriBool): TriBool;
begin
    Result := TVL_not(TVL_and(TVL_not(A), TVL_not(B)));
end;

function TVL_xor(A, B: TriBool): TriBool;
begin
    Result := TVL_and(TVL_or(A, B), TVL_not(TVL_or(A, B)));
end;

function TVL_eq(A, B: TriBool): TriBool;
begin
    Result := TVL_not(TVL_xor(A, B));
end;

end.

And that's the reason why you never on no account ever should compare against the values of True or False unless you intent ternary logic!

An alternative version would be using an enum type

type TriBool = (tbFalse, tbMaybe, tbTrue);

and defining a set of constants implementing the above tables:

const
    tvl_not: array[TriBool] = (tbTrue, tbMaybe, tbFalse);
    tvl_and: array[TriBool, TriBool] = (
        (tbFalse, tbFalse, tbFalse),
        (tbFalse, tbMaybe, tbMaybe),
        (tbFalse, tbMaybe, tbTrue),
        );
    tvl_or: array[TriBool, TriBool] = (
        (tbFalse, tbMaybe, tbTrue),
        (tbMaybe, tbMaybe, tbTrue),
        (tbTrue, tbTrue, tbTrue),
        );
    tvl_xor: array[TriBool, TriBool] = (
        (tbFalse, tbMaybe, tbTrue),
        (tbMaybe, tbMaybe, tbMaybe),
        (tbTrue, tbMaybe, tbFalse),
        );
    tvl_eq: array[TriBool, TriBool] = (
        (tbTrue, tbMaybe, tbFalse),
        (tbMaybe, tbMaybe, tbMaybe),
        (tbFalse, tbMaybe, tbTrue),
        );

That's no real fun, but lookup can then be done with

Result := tvl_and[A, B];

EasyLang

Translation of: FreeBASIC
sym$[] = [ "F" "?" "T" ]
arrbase sym$[] -1
# 
func tnot x .
   return -x
.
func tand x y .
   if x > y
      return tand y x
   .
   return x
.
func tor x y .
   if x < y
      return tor y x
   .
   return x
.
func teqv x y .
   return x * y
.
func timp x y .
   if -y > x
      return -y
   .
   return x
.
print "     (AND)      ( OR)      (EQV)      (IMP)     (NOT)"
print "     F ? T      F ? T      F ? T      F ? T          "
print "    -------------------------------------------------"
for i = -1 to 1
   o$ = " " & sym$[i] & " | "
   o$ &= sym$[tand -1 i] & " " & sym$[tand 0 i] & " " & sym$[tand 1 i]
   o$ &= "      "
   o$ &= sym$[tor -1 i] & " " & sym$[tor 0 i] & " " & sym$[tor 1 i]
   o$ &= "      "
   o$ &= sym$[timp -1 i] & " " & sym$[timp 0 i] & " " & sym$[timp 1 i]
   o$ &= "      "
   o$ &= sym$[timp -1 i] & " " & sym$[timp 0 i] & " " & sym$[timp 1 i]
   o$ &= "       " & sym$[tnot i]
   print o$
.

Elena

ELENA 6.x :

import extensions;
import system'routines;
import system'collections;
 
sealed class Trit
{
   bool _value;
 
   bool cast() = _value;
 
   constructor(object v)
   {
      if (v != nil)
      {
         _value := cast bool(v);
      }        
   }
 
   Trit equivalent(b)
   {
      var val2 := cast bool(b) \ back(nil);

      if (val2 != nil && _value != nil)
      {
         ^ _value.equal(val2)
      };

      ^  nilValue;
   }
 
   Trit Inverted
      = _value.Inverted \ back(nilValue);
 
   Trit and(b)
   {
      if (nil == _value)
      {
         ^ b.and(nil) \ back(nilValue)
      }
      else
      {
         ^ _value.and(b) \ back(nilValue)
      }
   }
 
   Trit or(b)
   {
      if (nil == _value)
      {
         ^ b.or(nilValue) \ back(nilValue)
      }
      else
      {
         ^ _value.or(b) \ back(nilValue)
      }
   }
 
   Trit implies(b)
      = self.Inverted.or(b);
 
   string toPrintable() = _value.toPrintable() \ back("maybe");
}
 
public program()
{
    List<Trit> values := new Trit[]{true, nilValue, false};
    values.forEach::(left)
    {
        console.printLine("¬",left," = ", left.Inverted);
        values.forEach::(right)
        {
            console.printLine(left, " & ", right, " = ", left && right);
            console.printLine(left, " | ", right, " = ", left || right);
            console.printLine(left, " → ", right, " = ", left.implies(right));
            console.printLine(left, " ≡ ", right, " = ", left.equivalent(right))
        }
    }
}
Output:
¬ true = false
true & true = true
true | true = true
true →  true = true
true ≡  true = true
true & maybe = maybe
true | maybe = true
true →  maybe = maybe
true ≡  maybe = maybe
true & false = false
true | false = true
true →  false = false
true ≡  false = false
¬ maybe = maybe
maybe & true = maybe
maybe | true = true
maybe →  true = true
maybe ≡  true = maybe
maybe & maybe = maybe
maybe | maybe = maybe
maybe →  maybe = maybe
maybe ≡  maybe = maybe
maybe & false = false
maybe | false = maybe
maybe →  false = maybe
maybe ≡  false = maybe
¬ false = true
false & true = false
false | true = true
false →  true = true
false ≡  true = false
false & maybe = false
false | maybe = maybe
false →  maybe = true
false ≡  maybe = maybe
false & false = false
false | false = false
false →  false = true
false ≡  false = true

Erlang

% Implemented by Arjun Sunel
-module(ternary).
-export([main/0, nott/1, andd/2,orr/2, then/2, equiv/2]).

main() ->
	{ok, [A]} = io:fread("Enter A: ","~s"),
	{ok, [B]} = io:fread("Enter B: ","~s"),
	andd(A,B).

nott(S) ->
	if 
		S=="T" ->
			io : format("F\n");

	 	S=="F" ->
			io : format("T\n");

		true ->
			io: format("?\n")
	end.	 
	
andd(A, B) ->
	if 
		A=="T", B=="T" ->
			io : format("T\n");
		
		A=="F"; B=="F" ->
			io : format("F\n");	

		true ->
			io: format("?\n")
	end.	


orr(A, B) ->
	if 
		A=="T"; B=="T" ->
			io : format("T\n");
		
		A=="?"; B=="?" ->
			io : format("?\n");	

		true ->
			io: format("F\n")
	end.
	

then(A, B) ->
	if 
		B=="T" ->
			io : format("T\n");
		
		A=="?" ->
			io : format("?\n");	

		A=="F" ->
			io :format("T\n");
		B=="F" ->
			io:format("F\n");	
		true ->
			io: format("?\n")
	end.	

equiv(A, B) ->
	if 
		A=="?" ->
			io : format("?\n");
		
		A=="F" ->
			io : format("~s\n", [nott(B)]);	

		true ->
			io: format("~s\n", [B])
	end.

Factor

For boolean logic, Factor uses t and f with the words >boolean, not, and, or, xor. For ternary logic, we add m and define the words >trit, tnot, tand, tor, txor and t=. Our new class, trit, is the union class of t, m and f.

! rosettacode/ternary/ternary.factor
! http://rosettacode.org/wiki/Ternary_logic
USING: combinators kernel ;
IN: rosettacode.ternary

SINGLETON: m
UNION: trit t m POSTPONE: f ;

GENERIC: >trit ( object -- trit )
M: trit >trit ;

: tnot ( trit1 -- trit )
    >trit { { t [ f ] } { m [ m ] } { f [ t ] } } case ;

: tand ( trit1 trit2 -- trit )
    >trit {
        { t [ >trit ] }
        { m [ >trit { { t [ m ] } { m [ m ] } { f [ f ] } } case ] }
        { f [ >trit drop f ] }
    } case ;

: tor ( trit1 trit2 -- trit )
    >trit {
        { t [ >trit drop t ] }
        { m [ >trit { { t [ t ] } { m [ m ] } { f [ m ] } } case ] }
        { f [ >trit ] }
    } case ;

: txor ( trit1 trit2 -- trit )
    >trit {
        { t [ tnot ] }
        { m [ >trit drop m ] }
        { f [ >trit ] }
    } case ;

: t= ( trit1 trit2 -- trit )
    {
        { t [ >trit ] }
        { m [ >trit drop m ] }
        { f [ tnot ] }
    } case ;

Example use:

( scratchpad ) CONSTANT: trits { t m f }
( scratchpad ) trits [ tnot ] map .
{ f m t }
( scratchpad ) trits [ trits swap [ tand ] curry map ] map .
{ { t m f } { m m f } { f f f } }
( scratchpad ) trits [ trits swap [ tor ] curry map ] map .
{ { t t t } { t m m } { t m f } }
( scratchpad ) trits [ trits swap [ txor ] curry map ] map .
{ { f m t } { m m m } { t m f } }
( scratchpad ) trits [ trits swap [ t= ] curry map ] map .
{ { t m f } { m m m } { f m t } }


Forth

Works with: gforth version 0.7.3

Standard Forth defines flags 'false' as 0 and 'true' as -1 (all bits set). We thus define 'maybe' as 1 to keep standard binary logic as-is and seamlessly include ternary logic. We may have use truthtables but here functions are more fluid.

1 constant maybe

: tnot dup maybe <> if invert then ;
: tand and ;
: tor or ;
: tequiv 2dup and rot tnot rot tnot and or ;
: timply tnot tor ;
: txor tequiv tnot ;

: t. C" TF?" 2 + + c@ emit ;

: table2. ( xt -- )
  cr ."     T F ?"
  cr ."   --------"
  2 true DO
    cr I t.  ."  | "
    2 true DO
      dup I J rot execute t. ."  "
    LOOP
  LOOP DROP ;

: table1. ( xt -- )
  2 true DO
    CR I t. ."  | "
    dup I swap execute t.
  LOOP DROP ;

CR ." [NOT]" ' tnot table1. CR
CR ." [AND]" ' tand table2. CR
CR ." [OR]" ' tor table2. CR
CR ." [XOR]" ' txor table2. CR
CR ." [IMPLY]" ' timply table2. CR
CR ." [EQUIV]" ' tequiv table2. CR
Output:
[NOT]
T | F
F | T
? | ?

[AND]
    T F ?
  --------
T | T F ? 
F | F F F 
? | ? F ? 

[OR]
    T F ?
  --------
T | T T T 
F | T F ? 
? | T ? ? 

[XOR]
    T F ?
  --------
T | F T ? 
F | T F ? 
? | ? ? ? 

[IMPLY]
    T F ?
  --------
T | T F ? 
F | T T T 
? | T ? ? 

[EQUIV]
    T F ?
  --------
T | T F ? 
F | F T ? 
? | ? ? ? 

As Forth is a concatenative language, ternary logic use appears seamless:

: optimist CR or if ." yes !" else ." no..." then ;   ok
true maybe optimist                                
yes ! ok
maybe false optimist                               
yes ! ok
maybe maybe optimist                               
yes ! ok
false false optimist                               
no... ok


Fortran

Please find the demonstration and compilation with gfortran at the start of the code. A module contains the ternary logic for easy reuse. Consider input redirection from unixdict.txt as vestigial. Or I could delete it.

!-*- mode: compilation; default-directory: "/tmp/" -*-
!Compilation started at Mon May 20 23:05:46
!
!a=./f && make $a && $a < unixdict.txt
!gfortran -std=f2003 -Wall -ffree-form f.f03 -o f
!
!ternary not
! 1.0 0.5 0.0
!
!
!ternary and
! 0.0 0.0 0.0
! 0.0 0.5 0.5
! 0.0 0.5 1.0
!
!
!ternary or
! 0.0 0.5 1.0
! 0.5 0.5 1.0
! 1.0 1.0 1.0
!
!
!ternary if
! 1.0 1.0 1.0
! 0.5 0.5 1.0
! 0.0 0.5 1.0
!
!
!ternary eq
! 1.0 0.5 0.0
! 0.5 0.5 0.5
! 0.0 0.5 1.0
!
!
!Compilation finished at Mon May 20 23:05:46


!This program is based on the j implementation
!not=: -.
!and=: <.
!or =: >.
!if =: (>. -.)"0~
!eq =: (<.&-. >. <.)"0

module trit

  real, parameter :: true = 1,  false = 0, maybe = 0.5

contains

  real function tnot(y)
    real, intent(in) :: y
    tnot = 1 - y
  end function tnot

  real function tand(x, y)
    real, intent(in) :: x, y
    tand = min(x, y)
  end function tand

  real function tor(x, y)
    real, intent(in) :: x, y
    tor = max(x, y)
  end function tor

  real function tif(x, y)
    real, intent(in) :: x, y
    tif = tor(y, tnot(x))
  end function tif

  real function teq(x, y)
    real, intent(in) :: x, y
    teq = tor(tand(tnot(x), tnot(y)), tand(x, y))
  end function teq

end module trit

program ternaryLogic
  use trit
  integer :: i
  real, dimension(3) :: a = [false, maybe, true] ! (/ ... /)
  write(6,'(/a)')'ternary not' ; write(6, '(3f4.1/)') (tnot(a(i)), i = 1 , 3)
  write(6,'(/a)')'ternary and' ; call table(tand, a, a)
  write(6,'(/a)')'ternary or' ; call table(tor, a, a)
  write(6,'(/a)')'ternary if' ; call table(tif, a, a)
  write(6,'(/a)')'ternary eq' ; call table(teq, a, a)

contains

  subroutine table(u, x, y) ! for now, show the table.
    real, external :: u
    real, dimension(3), intent(in) :: x, y
    integer :: i, j
    write(6, '(3(3f4.1/))') ((u(x(i), y(j)), j=1,3), i=1,3)
  end subroutine table

end program ternaryLogic

Free Pascal

Free Pascal version with lookup. Note equivalence and implication are used as proof, they are solved using the basic set instead of a lookup. Note Since we use a balanced range -1,0,1 multiplication equals EQU

{$mode objfpc}
unit ternarylogic;

interface
type
  { ternary type, balanced }
  trit = (tFalse=-1, tMaybe=0, tTrue=1);

 { ternary operators }
  
  { equivalence = multiplication }
  operator * (const a,b:trit):trit;
  operator and (const a,b:trit):trit;inline;
  operator or (const a,b:trit):trit;inline;
  operator not (const a:trit):trit;inline;
  operator xor (const a,b:trit):trit;
  { imp ==>}
  operator >< (const a,b:trit):trit;
                

implementation

  operator and (const a,b:trit):trit;inline;
    const lookupAnd:array[trit,trit] of trit =
                    ((tFalse,tFalse,tFalse),
                     (tFalse,tMaybe,tMaybe),
                     (tFalse,tMaybe,tTrue));
  begin
    Result:= LookupAnd[a,b];
  end;
         
  operator or (const a,b:trit):trit;inline;
    const lookupOr:array[trit,trit] of trit =
                   ((tFalse,tMaybe,tTrue),
                    (tMaybe,tMaybe,tTrue),
                    (tTrue,tTrue,tTrue));
  begin
    Result := LookUpOr[a,b];
  end;
 
  operator not (const a:trit):trit;inline;
    const LookupNot:array[trit] of trit =(tTrue,tMaybe,tFalse);
  begin
     Result:= LookUpNot[a];
  end;
 
  operator xor (const a,b:trit):trit;
    const LookupXor:array[trit,trit] of trit =
                    ((tFalse,tMaybe,tTrue),
                     (tMaybe,tMaybe,tMaybe),
                     (tTrue,tMaybe,tFalse));
  begin
    Result := LookupXor[a,b];
  end;

  operator * (const a,b:trit):trit;
  begin
    result := not (a xor b);
  end;

  { imp ==>}
  operator >< (const a,b:trit):trit;
  begin
     result := not(a) or b;
  end;
end.
program ternarytests;
{$mode objfpc}
uses 
  ternarylogic;   
begin
  writeln(' a AND b');
  writeln('F':7,'U':7, 'T':7);
  writeln('F|',tFalse and tFalse:7,tFalse and tMaybe:7,tFalse and tTrue:7);
  writeln('U|',tMaybe and tFalse:7,tMaybe and tMaybe:7,tMaybe and tTrue:7);
  writeln('T|',tTrue and tFalse:7,tTrue and tMaybe:7,tTrue and tTrue:7);
  writeln;
 
  writeln(' a OR b');
  writeln('F':7,'U':7, 'T':7);
  writeln('F|',tFalse or tFalse:7,tFalse or tMaybe:7,tFalse or tTrue:7);
  writeln('U|',tMaybe or tFalse:7,tMaybe or tMaybe:7,tMaybe or tTrue:7);
  writeln('T|',tTrue or tFalse:7,tTrue or tMaybe:7,tTrue or tTrue:7);
  writeln;
 
  writeln(' NOT a');
  writeln('F|',not tFalse:7);
  writeln('U|',not tMaybe:7);
  writeln('T|',not tTrue:7);
  writeln;
 
  writeln(' a XOR b');
  writeln('F':7,'U':7, 'T':7);
  writeln('F|',tFalse xor tFalse:7,tFalse xor tMaybe:7,tFalse xor tTrue:7);
  writeln('U|',tMaybe xor tFalse:7,tMaybe xor tMaybe:7,tMaybe xor tTrue:7);
  writeln('T|',tTrue xor tFalse:7,tTrue xor tMaybe:7,tTrue xor tTrue:7);
  writeln;
 
  writeln('equality/equivalence and multiplication');
  writeln('F':7,'U':7, 'T':7);
  writeln('F|', tFalse * tFalse:7,tFalse * tMaybe:7, tFalse * tTrue:7);
  writeln('U|', tMaybe * tFalse:7,tMaybe * tMaybe:7,tMaybe * tTrue:7);
  writeln('T|', tTrue * tFalse:7, tTrue * tMaybe:7, tTrue * tTrue:7);
   writeln;
 
  writeln('IMP. a.k.a. IfThen -> not(a) or b');
  writeln('F':7,'U':7, 'T':7);
  writeln('T|',tTrue >< tTrue:7,tTrue >< tMaybe:7,tTrue >< tFalse:7);
  writeln('U|',tMaybe >< tTrue:7,tMaybe >< tMaybe:7,tMaybe >< tFalse:7);
  writeln('F|',tFalse >< tTrue:7, tFalse >< tMaybe:7,tFalse >< tFalse:7);
  writeln;
end.
Output:
a AND b
      F      U      T
F|tFalse tFalse tFalse 
U|tFalse tMaybe tMaybe 
T|tFalse tMaybe tTrue  

 a OR b
      F      U      T
F|tFalse tMaybe tTrue  
U|tMaybe tMaybe tTrue  
T|tTrue  tTrue  tTrue  

 NOT a
F|tTrue  
U|tMaybe 
T|tFalse 

 a XOR b
      F      U      T
F|tFalse tMaybe tTrue  
U|tMaybe tMaybe tMaybe 
T|tTrue  tMaybe tFalse 

equality/equivalence and multiplication
      F      U      T
F|tTrue  tMaybe tFalse 
U|tMaybe tMaybe tMaybe 
T|tFalse tMaybe tTrue  

IMP. a.k.a. IfThen -> not(a) or b
      F      U      T
T|tTrue  tMaybe tFalse 
U|tTrue  tMaybe tMaybe 
F|tTrue  tTrue  tTrue

FreeBASIC

enum trit
   F=-1, M=0, T=1
end enum

dim as string symbol(-1 to 1) = {"F", "?", "T"}, outstr
dim as trit i

operator not ( x as trit ) as trit
    return -x
end operator

operator and (x as trit, y as trit) as trit
    if x>y then return y and x
    return x
end operator

operator or ( x as trit, y as trit ) as trit
    if x<y then return y or x
    return x
end operator

operator eqv ( x as trit, y as trit ) as trit
    return x*y 
end operator

operator imp ( x as trit, y as trit ) as trit
    if -y>x then return -y
    return x
end operator

print "     (AND)      ( OR)      (EQV)      (IMP)     (NOT)"
print "     F ? T      F ? T      F ? T      F ? T          "
print "    -------------------------------------------------"
for i = F to T
    outstr  = " "+symbol(i)+" | "
    outstr += symbol(F and i) + " " + symbol(M and i) + " " + symbol(T and i)
    outstr += "      "
    outstr += symbol(F or i) + " " + symbol(M or i) + " " + symbol(T or i)
    outstr += "      "
    outstr += symbol(F eqv i) + " " + symbol(M eqv i) + " " + symbol(T eqv i)
    outstr += "      "
    outstr += symbol(F imp i) + " " + symbol(M imp i) + " " + symbol(T imp i)
    outstr += "       " + symbol(not(i))
    print outstr
next i
     (AND)      ( OR)      (EQV)      (IMP)     (NOT)
     F ? T      F ? T      F ? T      F ? T          
    -------------------------------------------------
 F | F F F      F ? T      T ? F      T T T       T
 ? | F ? ?      ? ? T      ? ? ?      ? ? T       ?
 T | F ? T      T T T      F ? T      F ? T       F


Go

Go has four operators for the bool type: ==, &&, ||, and !.

package main

import "fmt"

type trit int8

const (
    trFalse trit = iota - 1
    trMaybe
    trTrue
)

func (t trit) String() string {
    switch t {
    case trFalse:
        return "False"
    case trMaybe:
        return "Maybe"
    case trTrue:
        return "True "
    }
    panic("Invalid trit")
}

func trNot(t trit) trit {
    return -t
}

func trAnd(s, t trit) trit {
    if s < t {
        return s
    }
    return t
}

func trOr(s, t trit) trit {
    if s > t {
        return s
    }
    return t
}

func trEq(s, t trit) trit {
    return s * t
}

func main() {
    trSet := []trit{trFalse, trMaybe, trTrue}

    fmt.Println("t     not t")
    for _, t := range trSet {
        fmt.Println(t, trNot(t))
    }

    fmt.Println("\ns     t     s and t")
    for _, s := range trSet {
        for _, t := range trSet {
            fmt.Println(s, t, trAnd(s, t))
        }
    }

    fmt.Println("\ns     t     s or t")
    for _, s := range trSet {
        for _, t := range trSet {
            fmt.Println(s, t, trOr(s, t))
        }
    }

    fmt.Println("\ns     t     s eq t")
    for _, s := range trSet {
        for _, t := range trSet {
            fmt.Println(s, t, trEq(s, t))
        }
    }
}
Output:
t     not t
False True 
Maybe Maybe
True  False

s     t     s and t
False False False
False Maybe False
False True  False
Maybe False False
Maybe Maybe Maybe
Maybe True  Maybe
True  False False
True  Maybe Maybe
True  True  True 

s     t     s or t
False False False
False Maybe Maybe
False True  True 
Maybe False Maybe
Maybe Maybe Maybe
Maybe True  True 
True  False True 
True  Maybe True 
True  True  True 

s     t     s eq t
False False True 
False Maybe Maybe
False True  False
Maybe False Maybe
Maybe Maybe Maybe
Maybe True  Maybe
True  False False
True  Maybe Maybe
True  True  True 

Groovy

Solution:

enum Trit {
    TRUE, MAYBE, FALSE
  
    private Trit nand(Trit that) {
        switch ([this,that]) {
            case { FALSE in it }: return TRUE
            case { MAYBE in it }: return MAYBE
            default             : return FALSE
        }
    }
    private Trit nor(Trit that) { this.or(that).not() }
  
    Trit and(Trit that)   { this.nand(that).not() }
    Trit or(Trit that)    { this.not().nand(that.not()) }
    Trit not()            { this.nand(this) }
    Trit imply(Trit that) { this.nand(that.not()) }
    Trit equiv(Trit that) { this.and(that).or(this.nor(that)) }
}

Test:

printf 'AND\n         '
Trit.values().each { b -> printf ('%6s', b) }
println '\n          ----- ----- -----'
Trit.values().each { a ->
    printf ('%6s | ', a)
    Trit.values().each { b -> printf ('%6s', a.and(b)) }
    println()
}

printf '\nOR\n         '
Trit.values().each { b -> printf ('%6s', b) }
println '\n          ----- ----- -----'
Trit.values().each { a ->
    printf ('%6s | ', a)
    Trit.values().each { b -> printf ('%6s', a.or(b)) }
    println()
}

println '\nNOT'
Trit.values().each {
    printf ('%6s | %6s\n', it, it.not())
}

printf '\nIMPLY\n         '
Trit.values().each { b -> printf ('%6s', b) }
println '\n          ----- ----- -----'
Trit.values().each { a ->
    printf ('%6s | ', a)
    Trit.values().each { b -> printf ('%6s', a.imply(b)) }
    println()
}

printf '\nEQUIV\n         '
Trit.values().each { b -> printf ('%6s', b) }
println '\n          ----- ----- -----'
Trit.values().each { a ->
    printf ('%6s | ', a)
    Trit.values().each { b -> printf ('%6s', a.equiv(b)) }
    println()
}
Output:
AND
           TRUE MAYBE FALSE
          ----- ----- -----
  TRUE |   TRUE MAYBE FALSE
 MAYBE |  MAYBE MAYBE FALSE
 FALSE |  FALSE FALSE FALSE

OR
           TRUE MAYBE FALSE
          ----- ----- -----
  TRUE |   TRUE  TRUE  TRUE
 MAYBE |   TRUE MAYBE MAYBE
 FALSE |   TRUE MAYBE FALSE

NOT
  TRUE |  FALSE
 MAYBE |  MAYBE
 FALSE |   TRUE

IMPLY
           TRUE MAYBE FALSE
          ----- ----- -----
  TRUE |   TRUE MAYBE FALSE
 MAYBE |   TRUE MAYBE MAYBE
 FALSE |   TRUE  TRUE  TRUE

EQUIV
           TRUE MAYBE FALSE
          ----- ----- -----
  TRUE |   TRUE MAYBE FALSE
 MAYBE |  MAYBE MAYBE MAYBE
 FALSE |  FALSE MAYBE  TRUE

Haskell

All operations given in terms of NAND, the functionally-complete operation.

import Prelude hiding (Bool(..), not, (&&), (||), (==))

main = mapM_ (putStrLn . unlines . map unwords)
    [ table "not"     $ unary not
    , table "and"     $ binary (&&)
    , table "or"      $ binary (||)
    , table "implies" $ binary (=->)
    , table "equals"  $ binary (==)
    ]

data Trit = False | Maybe | True deriving (Show)

False `nand` _     = True
_     `nand` False = True
True  `nand` True  = False
_     `nand` _     = Maybe

not a = nand a a

a && b = not $ a `nand` b

a || b = not a `nand` not b

a =-> b = a `nand` not b

a == b = (a && b) || (not a && not b)

inputs1 = [True, Maybe, False]
inputs2 = [(a,b) | a <- inputs1, b <- inputs1]

unary f = map (\a -> [a, f a]) inputs1
binary f = map (\(a,b) -> [a, b, f a b]) inputs2

table name xs = map (map pad) . (header :) $ map (map show) xs
    where header = map (:[]) (take ((length $ head xs) - 1) ['A'..]) ++ [name]

pad s = s ++ replicate (5 - length s) ' '
Output:
A     not
True  False
Maybe Maybe
False True

A     B     and
True  True  True
True  Maybe Maybe
True  False False
Maybe True  Maybe
Maybe Maybe Maybe
Maybe False False
False True  False
False Maybe False
False False False

A     B     or
True  True  True
True  Maybe True
True  False True
Maybe True  True
Maybe Maybe Maybe
Maybe False Maybe
False True  True
False Maybe Maybe
False False False

A     B     implies
True  True  True
True  Maybe Maybe
True  False False
Maybe True  True
Maybe Maybe Maybe
Maybe False Maybe
False True  True
False Maybe True
False False True

A     B     equals
True  True  True
True  Maybe Maybe
True  False False
Maybe True  Maybe
Maybe Maybe Maybe
Maybe False Maybe
False True  False
False Maybe Maybe
False False True

Icon and Unicon

The following example works in both Icon and Unicon. There are a couple of comments on the code that pertain to the task requirements:

  • Strictly speaking there are no binary values in Icon and Unicon. There are a number of flow control operations that result in expression success (and a result) or failure which affects flow. As a result there really isn't a set of binary operators to map into ternary. The example provides the minimum required by the task plus xor.
  • The code below does not define a data type as it doesn't really make sense in this case. Icon and Unicon can create records which would be overkill and clumsy in this case. Unicon can create objects which would also be overkill. The only remaining option is to reinterpret one of the existing types as ternary values. The code below implements balanced ternary values as integers in order to simplify several of the functions.
  • The use of integers doesn't really support strings of trits well. While there is a function showtrit to ease display a converse function to decode character trits in a string is not included.


$define TRUE    1
$define FALSE  -1
$define UNKNOWN 0 

invocable all
link printf

procedure main()  # demonstrate ternary logic

ufunc := ["not3"]
bfunc := ["and3", "or3", "xor3", "eq3", "ifthen3"]

every f := !ufunc  do {   # display unary functions
   printf("\nunary function=%s:\n",f)
   every t1 := (TRUE | FALSE | UNKNOWN) do
      printf(" %s : %s\n",showtrit(t1),showtrit(not3(t1)))
   }

   
every f :=  !bfunc do {   # display binary functions
   printf("\nbinary function=%s:\n     ",f)
   every t1 := (&null | TRUE | FALSE | UNKNOWN) do { 
      printf(" %s : ",showtrit(\t1))
      every t2 := (TRUE | FALSE | UNKNOWN | &null) do {
         if /t1 then printf("  %s",showtrit(\t2)|"\n")
         else printf("  %s",showtrit(f(t1,\t2))|"\n")
         }
      }
   }
end

procedure showtrit(a)   #: return printable trit of error if invalid
return case a of {TRUE:"T";FALSE:"F";UNKNOWN:"?";default:runerr(205,a)}
end

procedure istrit(a)     #: return value of trit or error if invalid
return (TRUE|FALSE|UNKNOWN|runerr(205,a)) = a 
end

procedure not3(a)       #: not of trit or error if invalid
return FALSE * istrit(a)
end

procedure and3(a,b)     #: and of two trits or error if invalid
return min(istrit(a),istrit(b))
end

procedure or3(a,b)      #: or of two trits or error if invalid
return max(istrit(a),istrit(b))
end

procedure eq3(a,b)      #: equals of two trits or error if invalid
return istrit(a) * istrit(b)
end

procedure ifthen3(a,b)  #: if trit then trit or error if invalid
return case istrit(a) of { TRUE: istrit(b) ; UNKNOWN: or3(a,b); FALSE: TRUE }
end

procedure xor3(a,b)     #: xor of two trits or error if invalid
return not3(eq3(a,b))
end

printf.icn provides support for the printf family of functions

Output:
unary function=not3:
 T : F
 F : T
 ? : ?

binary function=and3:
       T  F  ?
 T :   T  F  ?
 F :   F  F  F
 ? :   ?  F  ?

binary function=or3:
       T  F  ?
 T :   T  T  T
 F :   T  F  ?
 ? :   T  ?  ?

binary function=xor3:
       T  F  ?
 T :   F  T  ?
 F :   T  F  ?
 ? :   ?  ?  ?

binary function=eq3:
       T  F  ?
 T :   T  F  ?
 F :   F  T  ?
 ? :   ?  ?  ?

binary function=ifthen3:
       T  F  ?
 T :   T  F  ?
 F :   T  T  T
 ? :   T  ?  ?

J

The designers of J felt that user defined types were harmful, so that part of the task will not be supported here.

Instead:

true: 1 false: 0 maybe: 0.5

not=: -.
and=: <.
or =: >.
if =: (>. -.)"0~
eq =: (<.&-. >. <.)"0

Example use:

   not 0 0.5 1
1 0.5 0

   0 0.5 1 and/ 0 0.5 1
0   0   0
0 0.5 0.5
0 0.5   1

   0 0.5 1 or/ 0 0.5 1
  0 0.5 1
0.5 0.5 1
  1   1 1

   0 0.5 1 if/ 0 0.5 1
  1   1 1
0.5 0.5 1
  0 0.5 1

   0 0.5 1 eq/ 0 0.5 1
  1 0.5   0
0.5 0.5 0.5
  0 0.5   1

Note that this implementation is a special case of "fuzzy logic" (using a limited set of values).

Note that while >. and <. could be used for boolean operations instead of J's +. and *., the identity elements for >. and <. are not 1 and 0, but are negative and positive infinity. See also: Boolean ring

Note that we might instead define values between 0 and 1 to represent independent probabilities:

not=: -.
and=: *
or=: *&.-.
if =: (or -.)"0~
eq =: (*&-. or *)"0

However, while this might be a more intellectually satisfying approach, this gives us some different results from the task requirement, for the combination of two "maybe" values (we could "fix" this by adding some "logic" which replaced any non-integer value with "0.5" - this would satisfy literal compliance with the task specification and might even be a valid engineering choice if we are implementing in hardware, for example):

   not 0 0.5 1
1 0.5 0

   0 0.5 1 and/ 0 0.5 1
0    0   0
0 0.25 0.5
0  0.5   1

   0 0.5 1 or/ 0 0.5 1
  0  0.5 1
0.5 0.75 1
  1    1 1

   0 0.5 1 if/ 0 0.5 1
  1    1 1
0.5 0.75 1
  0  0.5 1

   0 0.5 1 eq/ 0 0.5 1
  1    0.5   0
0.5 0.4375 0.5
  0    0.5   1

Another interesting possibility would involve using George Boole's original operations. This leaves us without any "not", (if we include the definition of logical negation which was later added to the definition of Boolean algebra, then the only numbers which can be used with Boolean algebra are 1 and 0). So, it's not clear how we would implement "if" or "eq". However, "and" and "or" would look like this:

and=: *.
or=: +.

And, the boolean result tables would look like this:

   0 0.5 1 and/ 0 0.5 1
0   0 0
0 0.5 1
0   1 1

   0 0.5 1 or/ 0 0.5 1
  0 0.5   1
0.5 0.5 0.5
  1 0.5   1

Java

Works with: Java version 1.5+
public class Logic{
	public static enum Trit{
		TRUE, MAYBE, FALSE;
		
		public Trit and(Trit other){
			if(this == TRUE){
				return other;
			}else if(this == MAYBE){
				return (other == FALSE) ? FALSE : MAYBE;
			}else{
				return FALSE;
			}
		}
		
		public Trit or(Trit other){
			if(this == TRUE){
				return TRUE;
			}else if(this == MAYBE){
				return (other == TRUE) ? TRUE : MAYBE;
			}else{
				return other;
			}
		}
		
		public Trit tIf(Trit other){
			if(this == TRUE){
				return other;
			}else if(this == MAYBE){
				return (other == TRUE) ? TRUE : MAYBE;
			}else{
				return TRUE;
			}
		}
		
		public Trit not(){
			if(this == TRUE){
				return FALSE;
			}else if(this == MAYBE){
				return MAYBE;
			}else{
				return TRUE;
			}
		}
		
		public Trit equals(Trit other){
			if(this == TRUE){
				return other;
			}else if(this == MAYBE){
				return MAYBE;
			}else{
				return other.not();
			}
		}
	}
	public static void main(String[] args){
		for(Trit a:Trit.values()){
			System.out.println("not " + a + ": " + a.not());
		}
		for(Trit a:Trit.values()){
			for(Trit b:Trit.values()){
				System.out.println(a+" and "+b+": "+a.and(b)+
						"\t "+a+" or "+b+": "+a.or(b)+
						"\t "+a+" implies "+b+": "+a.tIf(b)+
						"\t "+a+" = "+b+": "+a.equals(b));
			}
		}
	}
}
Output:
not TRUE: FALSE
not MAYBE: MAYBE
not FALSE: TRUE
TRUE and TRUE: TRUE	 TRUE or TRUE: TRUE	 TRUE implies TRUE: TRUE	 TRUE = TRUE: TRUE
TRUE and MAYBE: MAYBE	 TRUE or MAYBE: TRUE	 TRUE implies MAYBE: MAYBE	 TRUE = MAYBE: MAYBE
TRUE and FALSE: FALSE	 TRUE or FALSE: TRUE	 TRUE implies FALSE: FALSE	 TRUE = FALSE: FALSE
MAYBE and TRUE: MAYBE	 MAYBE or TRUE: TRUE	 MAYBE implies TRUE: TRUE	 MAYBE = TRUE: MAYBE
MAYBE and MAYBE: MAYBE	 MAYBE or MAYBE: MAYBE	 MAYBE implies MAYBE: MAYBE	 MAYBE = MAYBE: MAYBE
MAYBE and FALSE: FALSE	 MAYBE or FALSE: MAYBE	 MAYBE implies FALSE: MAYBE	 MAYBE = FALSE: MAYBE
FALSE and TRUE: FALSE	 FALSE or TRUE: TRUE	 FALSE implies TRUE: TRUE	 FALSE = TRUE: FALSE
FALSE and MAYBE: FALSE	 FALSE or MAYBE: MAYBE	 FALSE implies MAYBE: TRUE	 FALSE = MAYBE: MAYBE
FALSE and FALSE: FALSE	 FALSE or FALSE: FALSE	 FALSE implies FALSE: TRUE	 FALSE = FALSE: TRUE

JavaScript

Let's use the trit already available in JavaScript: true, false (both boolean) and undefined…

var L3 = new Object();

L3.not = function(a) {
  if (typeof a == "boolean") return !a;
  if (a == undefined) return undefined;
  throw("Invalid Ternary Expression.");
}

L3.and = function(a, b) {
  if (typeof a == "boolean" && typeof b == "boolean") return a && b;
  if ((a == true && b == undefined) || (a == undefined && b == true)) return undefined;
  if ((a == false && b == undefined) || (a == undefined && b == false)) return false;
  if (a == undefined && b == undefined) return undefined;
  throw("Invalid Ternary Expression.");
}

L3.or = function(a, b) {
  if (typeof a == "boolean" && typeof b == "boolean") return a || b;
  if ((a == true && b == undefined) || (a == undefined && b == true)) return true;
  if ((a == false && b == undefined) || (a == undefined && b == false)) return undefined;
  if (a == undefined && b == undefined) return undefined;
  throw("Invalid Ternary Expression.");
}

// A -> B is equivalent to -A or B
L3.ifThen = function(a, b) {
  return L3.or(L3.not(a), b);
}

// A <=> B is equivalent to (A -> B) and (B -> A)
L3.iff = function(a, b) {
  return L3.and(L3.ifThen(a, b), L3.ifThen(b, a));
}

… and try these:

L3.not(true)         // false
L3.not(var a)        // undefined

L3.and(true, a)      // undefined

L3.or(a, 2 == 3)     // false

L3.ifThen(true, a)   // undefined

L3.iff(a, 2 == 2)    // undefined

Here is a compact solution

trit = {false: 'F', maybe: 'U', true: 'T'}
nand    = (a, b) => (a == trit.false || b == trit.false) ? trit.true : (a == trit.maybe || b == trit.maybe) ? trit.maybe : trit.false
not     = (a)    => nand(a, a)
and     = (a, b) => not(nand(a, b))
or      = (a, b) => nand(not(a), not(b))
nor     = (a, b) => not(or(a, b))
implies = (a, b) => nand(a, not(b))
iff     = (a, b) => or(and(a, b), nor(a, b))
xor     = (a, b) => not(iff(a, b))

... to test it

functor = {nand, and, or, nor, implies, iff, xor}
display = {nand: '⊼', and: '∧', or: '∨', nor: '⊽', implies: '⇒', iff: '⇔', xor: '⊻', not: '¬'}
values = Object.values(trit)

log = 'NOT\n';
for (let a of values) log += `${display.not}${a} = ${a}\n`

log += '\nNAND         AND          OR           NOR          IMPLIES       IFF           XOR'
for (let a of values) {
    for (let b of values) {
        log += "\n"
        for (let op in functor) log += `${a} ${display[op]} ${b} = ${functor[op](a, b)}    `
    }
}
console.log(log)

...Output:

NOT
¬F = F
¬U = U
¬T = T

NAND         AND          OR           NOR          IMPLIES       IFF           XOR
F ⊼ F = T    F ∧ F = F    F ∨ F = F    F ⊽ F = T    F ⇒ F = T    F ⇔ F = T    F ⊻ F = F    
F ⊼ U = T    F ∧ U = F    F ∨ U = U    F ⊽ U = U    F ⇒ U = T    F ⇔ U = U    F ⊻ U = U    
F ⊼ T = T    F ∧ T = F    F ∨ T = T    F ⊽ T = F    F ⇒ T = T    F ⇔ T = F    F ⊻ T = T    
U ⊼ F = T    U ∧ F = F    U ∨ F = U    U ⊽ F = U    U ⇒ F = U    U ⇔ F = U    U ⊻ F = U    
U ⊼ U = U    U ∧ U = U    U ∨ U = U    U ⊽ U = U    U ⇒ U = U    U ⇔ U = U    U ⊻ U = U    
U ⊼ T = U    U ∧ T = U    U ∨ T = T    U ⊽ T = F    U ⇒ T = T    U ⇔ T = U    U ⊻ T = U    
T ⊼ F = T    T ∧ F = F    T ∨ F = T    T ⊽ F = F    T ⇒ F = F    T ⇔ F = F    T ⊻ F = T    
T ⊼ U = U    T ∧ U = U    T ∨ U = T    T ⊽ U = F    T ⇒ U = U    T ⇔ U = U    T ⊻ U = U    
T ⊼ T = F    T ∧ T = T    T ∨ T = T    T ⊽ T = F    T ⇒ T = T    T ⇔ T = T    T ⊻ T = F

jq

jq itself does not have an extensible type system, so we'll use false, "maybe", and true as the three values since ternary logic agrees with Boolean logic for true and false, and because jq prints these three values consistently.

For consistency, all the ternary logic operators are defined here with the prefix "ternary_", but such a prefix is only needed for "not", "and", and "or", as these are jq keywords.
def ternary_nand(a; b):
  if a == false or b == false then true
  elif a == "maybe" or b == "maybe" then "maybe"
  else false
  end ;

def ternary_not(a):    ternary_nand(a; a);

def ternary_or(a; b):  ternary_nand( ternary_not(a); ternary_not(b) );

def ternary_nor(a; b): ternary_not( ternary_or(a;b) );

def ternary_and(a; b): ternary_not( ternary_nand(a; b) );

def ternary_imply(this; that):
  ternary_nand(this, ternary_not(that));

def ternary_equiv(this; that): 
  ternary_or( ternary_and(this; that); ternary_nor(this; that) );

def display_and(a; b):
  a as $a | b as $b 
  | "\($a) and \($b) is \( ternary_and($a; $b) )";
def display_equiv(a; b):
  a as $a | b as $b
  | "\($a) equiv \($b) is \( ternary_equiv($a; $b) )";
# etc etc

# Invoke the display functions:
display_and( (false, "maybe", true );  (false, "maybe", true) ),
display_equiv( (false, "maybe", true );  (false, "maybe", true) ),
"etc etc"
Output:
"false and false is false"
"false and maybe is false"
"false and true is false"
"maybe and false is false"
"maybe and maybe is maybe"
"maybe and true is maybe"
"true and false is false"
"true and maybe is maybe"
"true and true is true"
"false equiv false is true"
"false equiv maybe is maybe"
"false equiv true is false"
"maybe equiv false is maybe"
"maybe equiv maybe is maybe"
"maybe equiv true is maybe"
"true equiv false is false"
"true equiv maybe is maybe"
"true equiv true is true"
"etc etc"

Julia

Works with: Julia version 0.6
@enum Trit False Maybe True
const trits = (False, Maybe, True)

Base.:!(a::Trit) = a == False ? True : a == Maybe ? Maybe : False
(a::Trit, b::Trit) = a == b == True ? True : (a, b)  False ? False : Maybe
(a::Trit, b::Trit) = a == b == False ? False : (a, b)  True ? True : Maybe
(a::Trit, b::Trit) = a == False || b == True ? True : (a, b)  Maybe ? Maybe : False
(a::Trit, b::Trit) = (a, b)  Maybe ? Maybe : a == b ? True : False

println("Not (!):")
println(join(@sprintf("%10s%s is %5s", "!", t, !t) for t in trits))
println("And (∧):")
for a in trits
    println(join(@sprintf("%10s%5s is %5s", a, b, a  b) for b in trits))
end
println("Or (∨):")
for a in trits
    println(join(@sprintf("%10s%5s is %5s", a, b, a  b) for b in trits))
end
println("If Then (⊃):")
for a in trits
    println(join(@sprintf("%10s%5s is %5s", a, b, a  b) for b in trits))
end
println("Equivalent (≡):")
for a in trits
    println(join(@sprintf("%10s%5s is %5s", a, b, a  b) for b in trits))
end
Output:
Not (!):
         !False is  True         !Maybe is Maybe         !True is False
And (∧):
     False ∧ False is False     False ∧ Maybe is False     False ∧  True is False
     Maybe ∧ False is False     Maybe ∧ Maybe is Maybe     Maybe ∧  True is Maybe
      True ∧ False is False      True ∧ Maybe is Maybe      True ∧  True is  True
Or (∨):
     False ∨ False is False     False ∨ Maybe is Maybe     False ∨  True is  True
     Maybe ∨ False is Maybe     Maybe ∨ Maybe is Maybe     Maybe ∨  True is  True
      True ∨ False is  True      True ∨ Maybe is  True      True ∨  True is  True
If Then (⊃):
     False ⊃ False is  True     False ⊃ Maybe is  True     False ⊃  True is  True
     Maybe ⊃ False is Maybe     Maybe ⊃ Maybe is Maybe     Maybe ⊃  True is  True
      True ⊃ False is False      True ⊃ Maybe is Maybe      True ⊃  True is  True
Equivalent (≡):
     False ≡ False is  True     False ≡ Maybe is Maybe     False ≡  True is False
     Maybe ≡ False is Maybe     Maybe ≡ Maybe is Maybe     Maybe ≡  True is Maybe
      True ≡ False is False      True ≡ Maybe is Maybe      True ≡  True is  True


Alternative version

Works with: Julia version 0.7

With Julia 1.0 and the new type missing, three-value logic is implemented by default

# built-in: true, false and missing

using Printf

const tril = (true, missing, false)

@printf("\n%8s | %8s\n", "A", "¬A")
for A in tril
    @printf("%8s | %8s\n", A, !A)
end

@printf("\n%8s | %8s | %8s\n", "A", "B", "A ∧ B")
for (A, B) in Iterators.product(tril, tril)
    @printf("%8s | %8s | %8s\n", A, B, A & B)
end

@printf("\n%8s | %8s | %8s\n", "A", "B", "A ∨ B")
for (A, B) in Iterators.product(tril, tril)
    @printf("%8s | %8s | %8s\n", A, B, A | B)
end

@printf("\n%8s | %8s | %8s\n", "A", "B", "A ≡ B")
for (A, B) in Iterators.product(tril, tril)
    @printf("%8s | %8s | %8s\n", A, B, A == B)
end

(A, B) = B | !A

@printf("\n%8s | %8s | %8s\n", "A", "B", "A ⊃ B")
for (A, B) in Iterators.product(tril, tril)
    @printf("%8s | %8s | %8s\n", A, B, A  B)
end
Output:
       A |       ¬A
    true |    false
 missing |  missing
   false |     true

       A |        B |    A ∧ B
    true |     true |     true
 missing |     true |  missing
   false |     true |    false
    true |  missing |  missing
 missing |  missing |  missing
   false |  missing |    false
    true |    false |    false
 missing |    false |    false
   false |    false |    false

       A |        B |    A ∨ B
    true |     true |     true
 missing |     true |     true
   false |     true |     true
    true |  missing |     true
 missing |  missing |  missing
   false |  missing |  missing
    true |    false |     true
 missing |    false |  missing
   false |    false |    false

       A |        B |    A ≡ B
    true |     true |     true
 missing |     true |  missing
   false |     true |    false
    true |  missing |  missing
 missing |  missing |  missing
   false |  missing |  missing
    true |    false |    false
 missing |    false |  missing
   false |    false |     true

       A |        B |    A ⊃ B
    true |     true |     true
 missing |     true |     true
   false |     true |     true
    true |  missing |  missing
 missing |  missing |  missing
   false |  missing |     true
    true |    false |    false
 missing |    false |  missing

Kotlin

// version 1.1.2

enum class Trit {
    TRUE, MAYBE, FALSE;
 
    operator fun not() = when (this) {
        TRUE  -> FALSE
        MAYBE -> MAYBE
        FALSE -> TRUE
    }

    infix fun and(other: Trit) = when (this) {
        TRUE  -> other
        MAYBE -> if (other == FALSE) FALSE else MAYBE
        FALSE -> FALSE
    }

    infix fun or(other: Trit) = when (this) {
        TRUE  -> TRUE
        MAYBE -> if (other == TRUE) TRUE else MAYBE
        FALSE -> other
    }

    infix fun imp(other: Trit) = when (this) {
        TRUE  -> other
        MAYBE -> if (other == TRUE) TRUE else MAYBE
        FALSE -> TRUE
    }

    infix fun eqv(other: Trit) = when (this) {
        TRUE  -> other
        MAYBE -> MAYBE
        FALSE -> !other
    }

    override fun toString() = this.name[0].toString()
}

fun main(args: Array<String>) {
    val ta = arrayOf(Trit.TRUE, Trit.MAYBE, Trit.FALSE)

    // not
    println("not")
    println("-------")
    for (t in ta) println(" $t  | ${!t}")
    println()

    // and
    println("and | T  M  F")
    println("-------------")
    for (t in ta) {
        print(" $t  | ")
        for (tt in ta) print("${t and tt}  ")
        println()
    }
    println()

    // or
    println("or  | T  M  F")
    println("-------------")
    for (t in ta) {
        print(" $t  | ")
        for (tt in ta) print("${t or tt}  ")
        println()
    }
    println()

    // imp
    println("imp | T  M  F")
    println("-------------")
    for (t in ta) {
        print(" $t  | ")
        for (tt in ta) print("${t imp tt}  ")
        println()
    }
    println()

    // eqv
    println("eqv | T  M  F")
    println("-------------")
    for (t in ta) {
        print(" $t  | ")
        for (tt in ta) print("${t eqv tt}  ")
        println()
    }
}
Output:
not
-------
 T  | F
 M  | M
 F  | T

and | T  M  F
-------------
 T  | T  M  F  
 M  | M  M  F  
 F  | F  F  F  

or  | T  M  F
-------------
 T  | T  T  T  
 M  | T  M  M  
 F  | T  M  F  

imp | T  M  F
-------------
 T  | T  M  F  
 M  | T  M  M  
 F  | T  T  T  

eqv | T  M  F
-------------
 T  | T  M  F  
 M  | M  M  M  
 F  | F  M  T  

langur

Translation of: Go
# borrowing null for "maybe"
val .trSet = [false, null, true]

val .and = f switch[and] .a, .b {
    case true, null:
    case null, true:
    case null: null
    default: .a and .b
}

val .or = f switch[and] .a, .b {
    case false, null:
    case null, false:
    case null: null
    default: .a or .b
}

val .imply = f if(.a nor .b: not? .a; .b)

# formatting function for the result values
# replacing null with "maybe"
# using left alignment of 5 code points
val .F = f $"\{nn [.r, "maybe"]:-5}"

writeln "a     not a"
for .a in .trSet {
    writeln $"\.a:.F; \(not? .a:.F)"
}

writeln "\na     b     a and b"
for .a in .trSet {
    for .b in .trSet {
        writeln $"\.a:.F; \.b:.F; \.and(.a, .b):.F;"
    }
}

writeln "\na     b     a or b"
for .a in .trSet {
    for .b in .trSet {
        writeln $"\.a:.F; \.b:.F; \.or(.a, .b):.F;"
    }
}

writeln "\na     b     a implies b"
for .a in .trSet {
    for .b in .trSet {
        writeln $"\.a:.F; \.b:.F; \.imply(.a, .b):.F;"
    }
}

writeln "\na     b     a eq b"
for .a in .trSet {
    for .b in .trSet {
        writeln $"\.a:.F; \.b:.F; \.a ==? .b:.F;"
    }
}
Output:
a     not a
false true
maybe maybe
true  false

a     b     a and b
false false false
false maybe false
false true  false
maybe false false
maybe maybe maybe
maybe true  maybe
true  false false
true  maybe maybe
true  true  true

a     b     a or b
false false false
false maybe maybe
false true  true
maybe false maybe
maybe maybe maybe
maybe true  true
true  false true
true  maybe true
true  true  true

a     b     a implies b
false false true
false maybe true
false true  true
maybe false maybe
maybe maybe maybe
maybe true  true
true  false false
true  maybe maybe
true  true  true

a     b     a eq b
false false true
false maybe maybe
false true  false
maybe false maybe
maybe maybe maybe
maybe true  maybe
true  false false
true  maybe maybe
true  true  true 

Liberty BASIC

'ternary logic
'0 1 2
'F ? T
'False Don't know True
'LB has NOT AND OR XOR, so we implement them.
'LB has no EQ, but XOR could be expressed via EQ. In 'normal' boolean at least.

global tFalse, tDontKnow, tTrue
tFalse = 0
tDontKnow = 1
tTrue = 2

print "Short and long names for ternary logic values"
for i = tFalse to tTrue
    print shortName3$(i);" ";longName3$(i)
next
print

print "Single parameter functions"
print "x";" ";"=x";"  ";"not(x)"
for i = tFalse to tTrue
    print shortName3$(i);"  ";shortName3$(i);"    ";shortName3$(not3(i))
next
print

print "Double  parameter fuctions"
print "x";" ";"y";"  ";"x AND y";"  ";"x OR y";"  ";"x EQ y";"  ";"x XOR y"
for a = tFalse to tTrue
    for b = tFalse to tTrue
        print shortName3$(a);" ";shortName3$(b);"     "; _
            shortName3$(and3(a,b));"       "; shortName3$(or3(a,b));"       "; _
            shortName3$(eq3(a,b));"        "; shortName3$(xor3(a,b))
    next
next

function and3(a,b)
    and3 = min(a,b)
end function

function or3(a,b)
    or3 = max(a,b)
end function

function eq3(a,b)
    select case
    case a=tDontKnow or b=tDontKnow
        eq3 = tDontKnow
    case a=b
        eq3 = tTrue
    case else
        eq3 = tFalse
    end select
end function

function xor3(a,b)
    xor3 = not3(eq3(a,b))
end function

function not3(b)
    not3 = 2-b
end function

'------------------------------------------------
function shortName3$(i)
   shortName3$ = word$("F ? T", i+1)
end function

function longName3$(i)
    longName3$ = word$("False,Don't know,True", i+1, ",")
end function
Output:
Short and long names for ternary logic values
F False
? Don't know
T True

Single parameter functions
x =x  not(x)
F  F    T
?  ?    ?
T  T    F

Double  parameter fuctions
x y  x AND y  x OR y  x EQ y  x XOR y
F F     F       F       T        F
F ?     F       ?       ?        ?
F T     F       T       F        T
? F     F       ?       ?        ?
? ?     ?       ?       ?        ?
? T     ?       T       ?        ?
T F     F       T       F        T
T ?     ?       T       ?        ?
T T     T       T       T        F

M2000 Interpreter

module Ternary_logic {
	class trit {
	private:
		variant val
		function copy() {
			m=this
			m.trit
			=m
		}
	public:
		enum ternary {
			True="True"
			Maybe="Maybe"
			False="False"
		}
		function true() {
			=.copy(.True)
		}
		function maybe() {
			=.copy(.Maybe)
		}
		function false() {
			=.copy(.False)
		}
		operator "==" (k as trit) {
			push .val=k.val
		}
		operator "^^" (k as trit) {
			select enum .val
			case .True
				.val<=k.val
			case .False
				.val<=.False
			case else
				if  k.val=.False then .val<=.False else .val<=.Maybe
			end select
		}
		operator "^|" (k as trit) {
			select enum .val
			case .True
				.val<=k.val
			case .False
				.val<=.True
			case else
				if  k.val=.True then .val<=.True else .val<=.Maybe
			end select
		}	
		operator "||" (k as trit) {
			select enum .val
			case .False
				.val<=k.val
			case .True
				.val<=.True
			case else
				if  k.val=.True then .val<=.True else .val<=.Maybe
			end select
		}
		operator "~~" (k as trit) {
			select enum .val
			case .True
				.val<=k.val
			case .False
				if k.val=.True then .val<=.False else.if k.val=.False then .val<=.True else .val<=k.val
			case else
				.val<=.Maybe
			end select
		}	
		operator unary {
			select enum .val
			case .True
				.val<=.False
			case .False
				.val<=.True
			end select
		}
		group value {
			value {
				link parent val to val
				=val
			}
		}
		module trit {
			if empty or not isnum then
				read s as .ternary=.Maybe
				.val<=s
			else.if isnum then
				read what
				if what then
					.val<=.True
				else
					.val<=.False
				end if
			end if
		}
	}
	function enum2array(t) {
		m=each(t)
		while m {data eval(m)}
		=array([])
	}
	string out, nl={
	}
	q=trit()
	m=trit()
	k=enum2array(q.ternary)
	out ="not a" + nl
	a=each(k)
	while a
		q=trit(array(a))
		z=-q
		out +="    ternary_not "+(q.value) + " = " + (z.value) + nl
	end while
	out +="a and b" + nl
	a=each(k)
	while a
		b=each(k)
		while b
			q=trit(array(a))
			m=trit(array(b))
			z=q ^^ m
			out += "    " + (q.value) + " ternary_and " + (m.value) + " = " + (z.value) + nl
		end while
	end while
	out +="a or b" + nl
	a=each(k)
	while a
		b=each(k)
		while b
			q=trit(array(a))
			m=trit(array(b))
			z=q || m
			out += "    " + (q.value) + " ternary_or " + (m.value) + " = " + (z.value) + nl
		end while
	end while
	out +="if a then b" + nl
	a=each(k)
	while a
		b=each(k)
		while b
			q=trit(array(a))
			m=trit(array(b))
			z=q ^| m
			out += "    if " + (q.value) + " then " + (m.value) + " = " + (z.value) + nl
		end while
	end while
	out +="a is equivalent to b" + nl
	a=each(k)
	while a
		b=each(k)
		while b
			q=trit(array(a))
			m=trit(array(b))
			z=q ~~ m
			out += "    "+(q.value) + " is equivalent to " + (m.value) + " = " + (z.value) + nl
		end while
	end while
	report out
	clipboard out
}
Ternary_logic
Output:
not a
    ternary_not True = False
    ternary_not Maybe = Maybe
    ternary_not False = True
a and b
    True ternary_and True = True
    True ternary_and Maybe = Maybe
    True ternary_and False = False
    Maybe ternary_and True = Maybe
    Maybe ternary_and Maybe = Maybe
    Maybe ternary_and False = False
    False ternary_and True = False
    False ternary_and Maybe = False
    False ternary_and False = False
a or b
    True ternary_or True = True
    True ternary_or Maybe = True
    True ternary_or False = True
    Maybe ternary_or True = True
    Maybe ternary_or Maybe = Maybe
    Maybe ternary_or False = Maybe
    False ternary_or True = True
    False ternary_or Maybe = Maybe
    False ternary_or False = False
if a then b
    if True then True = True
    if True then Maybe = Maybe
    if True then False = False
    if Maybe then True = True
    if Maybe then Maybe = Maybe
    if Maybe then False = Maybe
    if False then True = True
    if False then Maybe = True
    if False then False = True
a is equivalent to b
    True is equivalent to True = True
    True is equivalent to Maybe = Maybe
    True is equivalent to False = False
    Maybe is equivalent to True = Maybe
    Maybe is equivalent to Maybe = Maybe
    Maybe is equivalent to False = Maybe
    False is equivalent to True = False
    False is equivalent to Maybe = Maybe
    False is equivalent to False = True

Maple

The logic system in Maple is implicitly ternary with truth values true, false, and FAIL.

The following script generates all truth tables for Maple logical operations. Note that in addition to the usual built-in logical operators for not, or, and, and xor, Maple also has implies.

tv := [true, false, FAIL];
NotTable     := Array(1..3, i->not tv[i] );
AndTable     := Array(1..3, 1..3, (i,j)->tv[i] and tv[j] );
OrTable      := Array(1..3, 1..3, (i,j)->tv[i] or tv[j] );
XorTable     := Array(1..3, 1..3, (i,j)->tv[i] xor tv[j] );
ImpliesTable := Array(1..3, 1..3, (i,j)->tv[i] implies tv[j] );
Output:
> tv := [true, false, FAIL];
                                     tv := [true, false, FAIL]

> NotTable := Array(1..3, i->not tv[i] );
                                  NotTable := [false, true, FAIL]

> AndTable := Array(1..3, 1..3, (i,j)->tv[i] and tv[j] );
                                           [true     false    FAIL ]
                                           [                       ]
                               AndTable := [false    false    false]
                                           [                       ]
                                           [FAIL     false    FAIL ]

> OrTable := Array(1..3, 1..3, (i,j)->tv[i] or tv[j] );
                                           [true    true     true]
                                           [                     ]
                                OrTable := [true    false    FAIL]
                                           [                     ]
                                           [true    FAIL     FAIL]

> XorTable := Array(1..3, 1..3, (i,j)->tv[i] xor tv[j] );
                                           [false    true     FAIL]
                                           [                      ]
                               XorTable := [true     false    FAIL]
                                           [                      ]
                                           [FAIL     FAIL     FAIL]

> ImpliesTable := Array(1..3, 1..3, (i,j)->tv[i] implies tv[j] );
                                              [true    false    FAIL]
                                              [                     ]
                              ImpliesTable := [true    true     true]
                                              [                     ]
                                              [true    FAIL     FAIL]

Mathematica/Wolfram Language

Type definition is not allowed in Mathematica. We can just use the build-in symbols "True" and "False", and add a new symbol "Maybe".

Maybe /: ! Maybe = Maybe;
Maybe /: (And | Or | Nand | Nor | Xor | Xnor | Implies | Equivalent)[Maybe, Maybe] = Maybe;

Example:

trits = {True, Maybe, False};
Print@Grid[
   ArrayFlatten[{{{{Not}}, {{Null}}}, {List /@ trits, 
      List /@ Not /@ trits}}]];
Do[Print@Grid[
   ArrayFlatten[{{{{operator}}, {{Null, Null, 
        Null}}}, {{{Null}}, {trits}}, {List /@ trits, 
      Outer[operator, trits, trits]}}]], {operator, {And, Or, Nand, 
   Nor, Xor, Xnor, Implies, Equivalent}}]
Output:
Not	
True	False
Maybe	Maybe
False	True

And			
	True	Maybe	False
True	True	Maybe	False
Maybe	Maybe	Maybe	False
False	False	False	False


Or			
	True	Maybe	False
True	True	True	True
Maybe	True	Maybe	Maybe
False	True	Maybe	False


Nand			
	True	Maybe	False
True	False	Maybe	True
Maybe	Maybe	Maybe	True
False	True	True	True


Nor			
	True	Maybe	False
True	False	False	False
Maybe	False	Maybe	Maybe
False	False	Maybe	True


Xor			
	True	Maybe	False
True	False	Maybe	True
Maybe	Maybe	Maybe	Maybe
False	True	Maybe	False


Xnor			
	True	Maybe	False
True	True	Maybe	False
Maybe	Maybe	Maybe	Maybe
False	False	Maybe	True



Implies			
	True	Maybe	False
True	True	Maybe	False
Maybe	True	Maybe	Maybe
False	True	True	True


Equivalent			
	True	Maybe	False
True	True	Maybe	False
Maybe	Maybe	Maybe	Maybe
False	False	Maybe	True

МК-61/52

П0	Сx	С/П	^	1	+	3	*	+	1
+	3	x^y	ИП0	<->	/	[x]	^	^	3
/	[x]	3	*	-	1	-	С/П	1	5
6	3	3	БП	00	1	9	5	6	9
БП	00	1	5	9	2	9	БП	00	1
5	6	6	5	БП	00	/-/	ЗН	С/П

Instruction:

БП XX С/П a ^ b С/П,

where XX = 28 for AND; 35 for OR; 42 for implies; 49 for equivalent; 56 for NOT;

a, b ∈ {-1, 0, 1}.

Nim

type Trit* = enum ttrue, tmaybe, tfalse

proc `$`*(a: Trit): string =
  case a
  of ttrue: "T"
  of tmaybe: "?"
  of tfalse: "F"

proc `not`*(a: Trit): Trit =
  case a
  of ttrue: tfalse
  of tmaybe: tmaybe
  of tfalse: ttrue

proc `and`*(a, b: Trit): Trit =
  const t: array[Trit, array[Trit, Trit]] =
    [ [ttrue,  tmaybe, tfalse]
    , [tmaybe, tmaybe, tfalse]
    , [tfalse, tfalse, tfalse] ]
  t[a][b]

proc `or`*(a, b: Trit): Trit =
  const t: array[Trit, array[Trit, Trit]] =
    [ [ttrue, ttrue,  ttrue]
    , [ttrue, tmaybe, tmaybe]
    , [ttrue, tmaybe, tfalse] ]
  t[a][b]

proc then*(a, b: Trit): Trit =
  const t: array[Trit, array[Trit, Trit]] =
    [ [ttrue, tmaybe, tfalse]
    , [ttrue, tmaybe, tmaybe]
    , [ttrue, ttrue,  ttrue] ]
  t[a][b]

proc equiv*(a, b: Trit): Trit =
  const t: array[Trit, array[Trit, Trit]] =
    [ [ttrue,  tmaybe, tfalse]
    , [tmaybe, tmaybe, tmaybe]
    , [tfalse, tmaybe, ttrue] ]
  t[a][b]


when isMainModule:

  import strutils

  for t in Trit:
    echo "Not ", t , ": ", not t

  for op1 in Trit:
    for op2 in Trit:
      echo "$# and   $#: $#".format(op1, op2, op1 and op2)
      echo "$# or    $#: $#".format(op1, op2, op1 or op2)
      echo "$# then  $#: $#".format(op1, op2, op1.then op2)
      echo "$# equiv $#: $#".format(op1, op2, op1.equiv op2)
Output:
Not T: F
Not ?: ?
Not F: T
T and   T: T
T or    T: T
T then  T: T
T equiv T: T
T and   ?: ?
T or    ?: T
T then  ?: ?
T equiv ?: ?
T and   F: F
T or    F: T
T then  F: F
T equiv F: F
? and   T: ?
? or    T: T
? then  T: T
? equiv T: ?
? and   ?: ?
? or    ?: ?
? then  ?: ?
? equiv ?: ?
? and   F: F
? or    F: ?
? then  F: ?
? equiv F: ?
F and   T: F
F or    T: T
F then  T: T
F equiv T: F
F and   ?: F
F or    ?: ?
F then  ?: T
F equiv ?: ?
F and   F: F
F or    F: F
F then  F: T
F equiv F: T

OCaml

type trit = True | False | Maybe

let t_not = function
  | True -> False
  | False -> True
  | Maybe -> Maybe

let t_and a b = match (a,b) with
   | (True,True) -> True
   | (False,_)  | (_,False) -> False
   | _ -> Maybe

let t_or a b = t_not (t_and (t_not a) (t_not b))

let t_eq a b = match (a,b) with
   | (True,True) | (False,False) -> True
   | (False,True) | (True,False) -> False
   | _ -> Maybe

let t_imply a b = t_or (t_not a) b

let string_of_trit = function
  | True -> "True"
  | False -> "False"
  | Maybe -> "Maybe"

let () =
  let values = [| True; Maybe; False |] in
  let f = string_of_trit in
  Array.iter (fun v -> Printf.printf "Not %s: %s\n" (f v) (f (t_not v))) values;
  print_newline ();
  let print op str =
    Array.iter (fun a ->
      Array.iter (fun b ->
        Printf.printf "%s %s %s: %s\n" (f a) str (f b) (f (op a b))
      ) values
    ) values;
    print_newline ()
  in
  print t_and "And";
  print t_or "Or";
  print t_imply "Then";
  print t_eq "Equiv";
;;
Output:
Not True: False
Not Maybe: Maybe
Not False: True

True And True: True
True And Maybe: Maybe
True And False: False
Maybe And True: Maybe
Maybe And Maybe: Maybe
Maybe And False: False
False And True: False
False And Maybe: False
False And False: False

True Or True: True
True Or Maybe: True
True Or False: True
Maybe Or True: True
Maybe Or Maybe: Maybe
Maybe Or False: Maybe
False Or True: True
False Or Maybe: Maybe
False Or False: False

True Then True: True
True Then Maybe: Maybe
True Then False: False
Maybe Then True: True
Maybe Then Maybe: Maybe
Maybe Then False: Maybe
False Then True: True
False Then Maybe: True
False Then False: True

True Equiv True: True
True Equiv Maybe: Maybe
True Equiv False: False
Maybe Equiv True: Maybe
Maybe Equiv Maybe: Maybe
Maybe Equiv False: Maybe
False Equiv True: False
False Equiv Maybe: Maybe
False Equiv False: True

Using a general binary -> ternary transform

Instead of writing all of the truth-tables by hand, we can construct a general binary -> ternary transform and apply it to any logical function we want:

type trit = True | False | Maybe

let to_bin = function True -> [true] | False -> [false] | Maybe -> [true;false]

let eval f x =
   List.fold_left (fun l c -> List.fold_left (fun m d -> ((d c) :: m)) l f) [] x

let rec from_bin =
   function [true] -> True | [false] -> False
   | h :: t -> (match (h, from_bin t) with
      (true,True) -> True | (false,False) -> False | _ -> Maybe)
   | _ -> Maybe

let to_ternary1 uop = fun x -> from_bin (eval [uop] (to_bin x))
let to_ternary2 bop = fun x y -> from_bin (eval (eval [bop] (to_bin x)) (to_bin y))

let t_not   = to_ternary1 (not)
let t_and   = to_ternary2 (&&)
let t_or    = to_ternary2 (||)
let t_equiv = to_ternary2 (=)
let t_imply = to_ternary2 (fun p q -> (not p) || q)

let str = function True -> "True " | False -> "False" | Maybe -> "Maybe"
let iterv f = List.iter f [True; False; Maybe]

let table1 s u =
   print_endline ("\n"^s^":");
   iterv (fun v -> print_endline ("  "^(str v)^" -> "^(str (u v))));;

let table2 s b =
   print_endline ("\n"^s^":");
   iterv (fun u ->
      iterv (fun v ->
         print_endline ("  "^(str u)^" "^(str v)^" -> "^(str (b u v)))));;

table1 "not" t_not;;
table2 "and" t_and;;
table2 "or" t_or;;
table2 "equiv" t_equiv;;
table2 "implies" t_imply;;
Output:
not:
  True  -> False
  False -> True 
  Maybe -> Maybe

and:
  True  True  -> True 
  True  False -> False
  True  Maybe -> Maybe
  False True  -> False
  False False -> False
  False Maybe -> False
  Maybe True  -> Maybe
  Maybe False -> False
  Maybe Maybe -> Maybe

or:
  True  True  -> True 
  True  False -> True 
  True  Maybe -> True 
  False True  -> True 
  False False -> False
  False Maybe -> Maybe
  Maybe True  -> True 
  Maybe False -> Maybe
  Maybe Maybe -> Maybe

equiv:
  True  True  -> True 
  True  False -> False
  True  Maybe -> Maybe
  False True  -> False
  False False -> True 
  False Maybe -> Maybe
  Maybe True  -> Maybe
  Maybe False -> Maybe
  Maybe Maybe -> Maybe

implies:
  True  True  -> True 
  True  False -> False
  True  Maybe -> Maybe
  False True  -> True 
  False False -> True 
  False Maybe -> True 
  Maybe True  -> True 
  Maybe False -> Maybe
  Maybe Maybe -> Maybe

ooRexx

tritValues = .array~of(.trit~true, .trit~false, .trit~maybe)
tab = '09'x

say "not operation (\)"
loop a over tritValues
    say "\"a":" (\a)
end

say
say "and operation (&)"
loop aa over tritValues
    loop bb over tritValues
        say (aa" & "bb":" (aa&bb))
    end
end

say
say "or operation (|)"
loop aa over tritValues
    loop bb over tritValues
        say (aa" | "bb":" (aa|bb))
    end
end

say
say "implies operation (&&)"
loop aa over tritValues
    loop bb over tritValues
        say (aa" && "bb":" (aa&&bb))
    end
end

say
say "equals operation (=)"
loop aa over tritValues
    loop bb over tritValues
        say (aa" = "bb":" (aa=bb))
    end
end

::class trit
-- making this a private method so we can control the creation
-- of these.  We only allow 3 instances to exist
::method new class private
  forward class(super)

::method init class
  expose true false maybe
  -- delayed creation
  true = .nil
  false = .nil
  maybe = .nil

-- read only attribute access to the instances.
-- these methods create the appropriate singleton on the first call
::attribute true class get
  expose true
  if true == .nil then true = self~new("True")
  return true

::attribute false class get
  expose false
  if false == .nil then false = self~new("False")
  return false

::attribute maybe class get
  expose maybe
  if maybe == .nil then maybe = self~new("Maybe")
  return maybe

-- create an instance
::method init
  expose value
  use arg value

-- string method to return the value of the instance
::method string
  expose value
  return value

-- "and" method using the operator overload
::method "&"
  use strict arg other
  if self == .trit~true then return other
  else if self == .trit~maybe then do
      if other == .trit~false then return .trit~false
      else return .trit~maybe
  end
  else return .trit~false

-- "or" method using the operator overload
::method "|"
  use strict arg other
  if self == .trit~true then return .trit~true
  else if self == .trit~maybe then do
      if other == .trit~true then return .trit~true
      else return .trit~maybe
  end
  else return other

-- implies method...using the XOR operator for this
::method "&&"
  use strict arg other
  if self == .trit~true then return other
  else if self == .trit~maybe then do
      if other == .trit~true then return .trit~true
      else return .trit~maybe
  end
  else return .trit~true

-- "not" method using the operator overload
::method "\"
  if self == .trit~true then return .trit~false
  else if self == .trit~maybe then return .trit~maybe
  else return .trit~true

-- "equals" using the "=" override.  This makes a distinction between
-- the "==" operator, which is real equality and the "=" operator, which
-- is trinary equality.
::method "="
  use strict arg other
  if self == .trit~true then return other
  else if self == .trit~maybe then return .trit~maybe
  else return \other
not operation (\)
\True: False
\False: True
\Maybe: Maybe

and operation (&)
True & True: True
True & False: False
True & Maybe: Maybe
False & True: False
False & False: False
False & Maybe: False
Maybe & True: Maybe
Maybe & False: False
Maybe & Maybe: Maybe

or operation (|)
True | True: True
True | False: True
True | Maybe: True
False | True: True
False | False: False
False | Maybe: Maybe
Maybe | True: True
Maybe | False: Maybe
Maybe | Maybe: Maybe

implies operation (&&)
True && True: True
True && False: False
True && Maybe: Maybe
False && True: True
False && False: True
False && Maybe: True
Maybe && True: True
Maybe && False: Maybe
Maybe && Maybe: Maybe

equals operation (=)
True = True: True
True = False: False
True = Maybe: Maybe
False = True: False
False = False: True
False = Maybe: Maybe
Maybe = True: Maybe
Maybe = False: Maybe
Maybe = Maybe: Maybe

Pascal

Program TernaryLogic (output);

type
  trit = (terTrue, terMayBe, terFalse);

function terNot (a: trit): trit;
  begin
    case a of
      terTrue:  terNot := terFalse;
      terMayBe: terNot := terMayBe;
      terFalse: terNot := terTrue;
    end;
  end;

function terAnd (a, b: trit): trit;
  begin
    terAnd := terMayBe;
    if (a = terFalse) or (b = terFalse) then
      terAnd := terFalse
    else
      if (a = terTrue) and (b = terTrue) then
        terAnd := terTrue;
  end;

function terOr (a, b: trit): trit;
  begin
    terOr := terMayBe;
    if (a = terTrue) or (b = terTrue) then
      terOr := terTrue
    else
      if (a = terFalse) and (b = terFalse) then
        terOr := terFalse;
  end;

function terEquals (a, b: trit): trit;
  begin
    if a = b then
      terEquals := terTrue
    else
      if a <> b then
        terEquals := terFalse;
    if (a = terMayBe) or (b = terMayBe) then
      terEquals := terMayBe;
  end;

function terIfThen (a, b: trit): trit;
  begin
    terIfThen := terMayBe;
    if (a = terTrue) or (b = terFalse)  then
      terIfThen := terTrue
    else
      if (a = terFalse) and (b = terTrue) then
        terIfThen := terFalse;
  end;

function terToStr(a: trit): string;
  begin
    case a of
      terTrue:  terToStr := 'True ';
      terMayBe: terToStr := 'Maybe';
      terFalse: terToStr := 'False';
    end;
  end;

begin
  writeln('Ternary logic test:');
  writeln;
  writeln('NOT ', ' True ', ' Maybe', ' False');
  writeln('     ', terToStr(terNot(terTrue)), ' ', terToStr(terNot(terMayBe)), ' ', terToStr(terNot(terFalse)));
  writeln;
  writeln('AND   ', ' True ', ' Maybe', ' False');
  writeln('True   ', terToStr(terAnd(terTrue,terTrue)),  ' ', terToStr(terAnd(terMayBe,terTrue)),  ' ', terToStr(terAnd(terFalse,terTrue)));
  writeln('Maybe  ', terToStr(terAnd(terTrue,terMayBe)), ' ', terToStr(terAnd(terMayBe,terMayBe)), ' ', terToStr(terAnd(terFalse,terMayBe)));
  writeln('False  ', terToStr(terAnd(terTrue,terFalse)), ' ', terToStr(terAnd(terMayBe,terFalse)), ' ', terToStr(terAnd(terFalse,terFalse)));
  writeln;
  writeln('OR    ', ' True ', ' Maybe', ' False');
  writeln('True   ', terToStr(terOR(terTrue,terTrue)),  ' ', terToStr(terOR(terMayBe,terTrue)),  ' ', terToStr(terOR(terFalse,terTrue)));
  writeln('Maybe  ', terToStr(terOR(terTrue,terMayBe)), ' ', terToStr(terOR(terMayBe,terMayBe)), ' ', terToStr(terOR(terFalse,terMayBe)));
  writeln('False  ', terToStr(terOR(terTrue,terFalse)), ' ', terToStr(terOR(terMayBe,terFalse)), ' ', terToStr(terOR(terFalse,terFalse)));
  writeln;
  writeln('IFTHEN', ' True ', ' Maybe', ' False');
  writeln('True   ', terToStr(terIfThen(terTrue,terTrue)),  ' ', terToStr(terIfThen(terMayBe,terTrue)),  ' ', terToStr(terIfThen(terFalse,terTrue)));
  writeln('Maybe  ', terToStr(terIfThen(terTrue,terMayBe)), ' ', terToStr(terIfThen(terMayBe,terMayBe)), ' ', terToStr(terIfThen(terFalse,terMayBe)));
  writeln('False  ', terToStr(terIfThen(terTrue,terFalse)), ' ', terToStr(terIfThen(terMayBe,terFalse)), ' ', terToStr(terIfThen(terFalse,terFalse)));
  writeln;
  writeln('EQUAL ', ' True ', ' Maybe', ' False');
  writeln('True   ', terToStr(terEquals(terTrue,terTrue)),  ' ', terToStr(terEquals(terMayBe,terTrue)),  ' ', terToStr(terEquals(terFalse,terTrue)));
  writeln('Maybe  ', terToStr(terEquals(terTrue,terMayBe)), ' ', terToStr(terEquals(terMayBe,terMayBe)), ' ', terToStr(terEquals(terFalse,terMayBe)));
  writeln('False  ', terToStr(terEquals(terTrue,terFalse)), ' ', terToStr(terEquals(terMayBe,terFalse)), ' ', terToStr(terEquals(terFalse,terFalse)));
  writeln;
end.
Output:
:> ./TernaryLogic
Ternary logic test:

NOT  True  Maybe False
     False Maybe True 

AND    True  Maybe False
True   True  Maybe False
Maybe  Maybe Maybe False
False  False False False

OR     True  Maybe False
True   True  True  True 
Maybe  True  Maybe Maybe
False  True  Maybe False

IFTHEN True  Maybe False
True   True  Maybe False
Maybe  True  Maybe Maybe
False  True  True  True 

EQUAL  True  Maybe False
True   True  Maybe False
Maybe  Maybe Maybe Maybe
False  False Maybe True 

Perl

use v5.36;

package Trit;
use List::Util qw(min max);

our @ISA = qw(Exporter);
our @EXPORT = qw(%E);

my %E = (true => 1, false => -1, maybe => 0);

use overload
'<=>' => sub ($a,$b)       { $a->cmp($b)   },
'cmp' => sub ($a,$b)       { $a->cmp($b)   },
'=='  => sub ($a,$b,$)     { $$a == $$b    },
'eq'  => sub ($a,$b,$)     { $a->equiv($b) },
'>'   => sub ($a,$b,$)     { $$a >  $E{$b} },
'<'   => sub ($a,$b,$)     { $$a <  $E{$b} },
'>='  => sub ($a,$b,$)     { $$a >= $$b    },
'<='  => sub ($a,$b,$)     { $$a <= $$b    },
'|'   => sub ($a,$b,$,$,$) { $a->or($b)    },
'&'   => sub ($a,$b,$,$,$) { $a->and($b)   },
'!'   => sub ($a,$,$)      { $a->not()     },
'~'   => sub ($a,$,$,$,$)  { $a->not()     },
'neg' => sub ($a,$,$)      { -$$a          },
'""'  => sub ($a,$,$)      { $a->tostr()   },
'0+'  => sub ($a,$,$)      { $a->tonum()   },
;

sub eqv ($a,$b) {
    $$a == $E{maybe} || $E{$b} == $E{maybe} ? $E{maybe} :       # either arg 'maybe', return 'maybe'
    $$a == $E{false} && $E{$b} == $E{false} ? $E{true}  :       #  both args 'false', return 'true'
                                              min $$a, $E{$b}   # either arg 'false', return 'false', otherwise 'true'
}

# do tests in a manner that avoids overloaded operators
sub new ($class, $v) {
    my $value =
        ! defined $v    ? $E{maybe} :
        $v =~ /true/    ? $E{true}  :
        $v =~ /false/   ? $E{false} :
        $v =~ /maybe/   ? $E{maybe} :
        $v gt $E{maybe} ? $E{true}  :
        $v lt $E{maybe} ? $E{false} :
                          $E{maybe} ;
    bless \$value, $class;
}

sub tostr ($a) { $$a > $E{maybe} ? 'true' : $$a < $E{maybe} ? 'false' : 'maybe' }
sub tonum ($a) { $$a }

sub not   ($a)    { Trit->new( -$a        ) }
sub cmp   ($a,$b) { Trit->new( $a <=> $b  ) }
sub and   ($a,$b) { Trit->new( min $a, $b ) }
sub or    ($a,$b) { Trit->new( max $a, $b ) }
sub equiv ($a,$b) { Trit->new( eqv $a, $b ) }

package main;
Trit->import;

my @a = ( Trit->new($E{true}), Trit->new($E{maybe}), Trit->new($E{false}) );
printf "Codes for logic values: %6s = %d %6s = %d %6s = %d\n", @a[0, 0, 1, 1, 2, 2];

# prefix ! (not) ['~' also can be used]
say "\na\tNOT a";
print "$_\t".(!$_)."\n" for @a;

# infix & (and)
say "\nAND\t" . join("\t",@a);
for my $a (@a) { print $a; print "\t" . ($a & $_)  for @a; say '' }

# infix | (or)
say "\nOR\t" . join("\t",@a);
for my $a (@a) { print $a; print "\t" . ($a | $_)  for @a; say '' }

# infix eq (equivalence)
say "\nEQV\t" . join("\t",@a);
for my $a (@a) { print $a; print "\t" . ($a eq $_) for @a; say '' }

# infix == (equality)
say "\n==\t" . join("\t",@a);
for my $a (@a) { print $a; print "\t" . ($a == $_) for @a; say '' }
Output:
Codes for logic values:   true = 1  maybe = 0  false = -1

a	NOT a
true	false
maybe	maybe
false	true

AND	true	maybe	false
true	true	maybe	false
maybe	maybe	maybe	false
false	false	false	false

OR	true	maybe	false
true	true	true	true
maybe	true	maybe	maybe
false	true	maybe	false

EQV	true	maybe	false
true	true	maybe	false
maybe	maybe	maybe	maybe
false	false	maybe	true

==	true	maybe	false
true	1		
maybe		1	
false			1

Phix

Library: Phix/basics
enum T, M, F
type ternary(integer t) return find(t,{T,M,F}) end type
 
function t_not(ternary a)
    return F+1-a
end function
 
function t_and(ternary a, ternary b)
    return iff(a=T and b=T?T:iff(a=F or b=F?F:M))   
end function
 
function t_or(ternary a, ternary b)
    return iff(a=T or b=T?T:iff(a=F and b=F?F:M))   
end function
 
function t_xor(ternary a, ternary b)
    return iff(a=M or b=M?M:iff(a=b?F:T))
end function
 
function t_implies(ternary a, ternary b)
    return iff(a=F or b=T?T:iff(a=T and b=F?F:M))   
end function
 
function t_equal(ternary a, ternary b)
    return iff(a=M or b=M?M:iff(a=b?T:F))
end function
 
function t_string(ternary a)
    return iff(a=T?"T":iff(a=M?"?":"F"))
end function
 
procedure show_truth_table(integer rid, integer unary, string name)
    printf(1,"%-3s |%s\n",{name,iff(unary?"":" T | ? | F")})
    printf(1,"----+---%s\n",{iff(unary?"":"+---+---")})
    for x=T to F do
        printf(1," %s ",{t_string(x)})
        if unary then
            printf(1," | %s",{t_string(rid(x))})
        else
            for y=T to F do
                printf(1," | %s",{t_string(rid(x,y))})
            end for
        end if
        printf(1,"\n")
    end for
    printf(1,"\n")
end procedure
 
show_truth_table(t_not,1,"not")
show_truth_table(t_and,0,"and")
show_truth_table(t_or,0,"or")
show_truth_table(t_xor,0,"xor")
show_truth_table(t_implies,0,"imp")
show_truth_table(t_equal,0,"eq")
Output:
not |
----+---
 T  | F
 ?  | ?
 F  | T

and | T | ? | F
----+---+---+---
 T  | T | ? | F
 ?  | ? | ? | F
 F  | F | F | F

or  | T | ? | F
----+---+---+---
 T  | T | T | T
 ?  | T | ? | ?
 F  | T | ? | F

xor | T | ? | F
----+---+---+---
 T  | F | ? | T
 ?  | ? | ? | ?
 F  | T | ? | F

imp | T | ? | F
----+---+---+---
 T  | T | ? | F
 ?  | T | ? | ?
 F  | T | T | T

eq  | T | ? | F
----+---+---+---
 T  | T | ? | F
 ?  | ? | ? | ?
 F  | F | ? | T

PHP

Save the sample code as executable shell script on your *nix system:

#!/usr/bin/php
<?php

# defined as numbers, so I can use max() and min() on it
if (! define('triFalse',0))  trigger_error('Unknown error defining!', E_USER_ERROR);
if (! define('triMaybe',1))  trigger_error('Unknown error defining!', E_USER_ERROR);
if (! define('triTrue', 2))  trigger_error('Unknown error defining!', E_USER_ERROR);

$triNotarray = array(triFalse=>triTrue, triMaybe=>triMaybe, triTrue=>triFalse);

# output helper
function triString ($tri) {
    if ($tri===triFalse) return 'false  ';
    if ($tri===triMaybe) return 'unknown';
    if ($tri===triTrue)  return 'true   ';
    trigger_error('triString: parameter not a tri value', E_USER_ERROR);
}

function triAnd() {
    if (func_num_args() < 2) 
       trigger_error('triAnd needs 2 or more parameters', E_USER_ERROR);
    return min(func_get_args());
}

function triOr() {
    if (func_num_args() < 2) 
       trigger_error('triOr needs 2 or more parameters', E_USER_ERROR);
    return max(func_get_args());
}

function triNot($t) {
    global $triNotarray; # using result table
    if (in_array($t, $triNotarray)) return $triNotarray[$t];
    trigger_error('triNot: Parameter is not a tri value', E_USER_ERROR);
}

function triImplies($a, $b) {
    if ($a===triFalse || $b===triTrue)  return triTrue;
    if ($a===triMaybe || $b===triMaybe) return triMaybe;
    # without parameter type check I just would return triFalse here
    if ($a===triTrue &&  $b===triFalse) return triFalse;
    trigger_error('triImplies: parameter type error', E_USER_ERROR);
}

function triEquiv($a, $b) {
    if ($a===triTrue)  return $b;
    if ($a===triMaybe) return $a;
    if ($a===triFalse) return triNot($b);
    trigger_error('triEquiv: parameter type error', E_USER_ERROR);
}

# data sampling

printf("--- Sample output for a equivalent b ---\n\n");

foreach ([triTrue,triMaybe,triFalse] as $a) {
    foreach ([triTrue,triMaybe,triFalse] as $b) {
        printf("for a=%s and b=%s a equivalent b is %s\n",
               triString($a), triString($b), triString(triEquiv($a, $b)));
    }
}

Sample output:

--- Sample output for a equivalent b ---

for a=true    and b=true    a equivalent b is true   
for a=true    and b=unknown a equivalent b is unknown
for a=true    and b=false   a equivalent b is false  
for a=unknown and b=true    a equivalent b is unknown
for a=unknown and b=unknown a equivalent b is unknown
for a=unknown and b=false   a equivalent b is unknown
for a=false   and b=true    a equivalent b is false  
for a=false   and b=unknown a equivalent b is unknown
for a=false   and b=false   a equivalent b is true   

Picat

main =>
  (show_op1('!') ; true),
  nl,
  foreach(Op in ['/\\','\\/','->','=='])
     (show_op2(Op) ; nl,true)
  end.

ternary(true,'!') = false.
ternary(maybe,'!') = maybe.
ternary(false,'!') = true.

ternary(Cond,'!') = Res =>
  C1 = cond(Cond == maybe,maybe,cond(Cond,true,false)),
  Res = ternary(C1,'!').

ternary(true,'/\\',A)   = A.
ternary(maybe,'/\\',A)  = cond(A==false,false,maybe).
ternary(false,'/\\',_A) = false.

ternary(true,'\\/',_A) = true.
ternary(maybe,'\\/',A) = cond(A==true,true, maybe).
ternary(false,'\\/',A) = A.

ternary(true,'->',A)  = A.
ternary(maybe,'->',A) = cond(A==true,true,maybe).
ternary(false,'->',_) = true.

ternary(true,'==',A)  = A.
ternary(maybe,'==',_) = maybe.
ternary(false,'==',A) = ternary(A,'!').

ternary(Cond1,Op,Cond2) = Res =>
  C1 = cond(Cond1 == maybe,maybe,cond(Cond1,true,false)),
  C2 = cond(Cond2 == maybe,maybe,cond(Cond2,true,false)),
  Res = ternary(C1,Op,C2).

show_op1(Op) =>
  println(Op),
  println(['_' : _ in 1..11]),  
  foreach(V1 in [true,maybe,false])
    V2 = ternary(V1,Op),
    printf("%5w %5w \n",V1,V2)
  end,
  nl.

show_op2(Op) =>
  Vs = [true,maybe,false],
  printf("%2w      %5w %5w %5w\n",Op,Vs[1],Vs[2],Vs[3]),
  println(['_' : _ in 1..25]),
  foreach(V1 in Vs)
    printf("%-5w | ", V1),
    foreach(V2 in Vs)
      C = ternary(V1,Op,V2),
      printf("%5w ",C)
    end,
    nl
  end,
  nl.
Output:
!
___________
 true false 
maybe maybe 
false  true 


/\       true maybe false
_________________________
true  |  true maybe false 
maybe | maybe maybe false 
false | false false false 

\/       true maybe false
_________________________
true  |  true  true  true 
maybe |  true maybe maybe 
false |  true maybe false 

->       true maybe false
_________________________
true  |  true maybe false 
maybe |  true maybe maybe 
false |  true  true  true 

==       true maybe false
_________________________
true  |  true maybe false 
maybe | maybe maybe maybe 
false | false maybe  true 

Simple examples

main =>
  println(ternary(10 > 3,'->',maybe).ternary('!')),
  println(ternary(4 < 18,'/\\',$not membchk('a',"picat")).ternary('->',maybe).ternary('==',true)).
Output:
maybe
true

PicoLisp

In addition for the standard T (for "true") and NIL (for "false") we define 0 (zero, for "maybe").

(de 3not (A)
   (or (=0 A) (not A)) )

(de 3and (A B)
   (cond
      ((=T A) B)
      ((=0 A) (and B 0)) ) )

(de 3or (A B)
   (cond
      ((=T A) T)
      ((=0 A) (or (=T B) 0))
      (T B) ) )

(de 3impl (A B)
   (cond
      ((=T A) B)
      ((=0 A) (or (=T B) 0))
      (T T) ) )

(de 3equiv (A B)
   (cond
      ((=T A) B)
      ((=0 A) 0)
      (T (3not B)) ) )

Test:

(for X '(T 0 NIL)
   (println 'not X '-> (3not X)) )

(for Fun '((and . 3and) (or . 3or) (implies . 3impl) (equivalent . 3equiv))
   (for X '(T 0 NIL)
      (for Y '(T 0 NIL)
         (println X (car Fun) Y '-> ((cdr Fun) X Y)) ) ) )
Output:
not T -> NIL
not 0 -> 0
not NIL -> T
T and T -> T
T and 0 -> 0
T and NIL -> NIL
0 and T -> 0
0 and 0 -> 0
0 and NIL -> NIL
NIL and T -> NIL
NIL and 0 -> NIL
NIL and NIL -> NIL
T or T -> T
T or 0 -> T
T or NIL -> T
0 or T -> T
0 or 0 -> 0
0 or NIL -> 0
NIL or T -> T
NIL or 0 -> 0
NIL or NIL -> NIL
T implies T -> T
T implies 0 -> 0
T implies NIL -> NIL
0 implies T -> T
0 implies 0 -> 0
0 implies NIL -> 0
NIL implies T -> T
NIL implies 0 -> T
NIL implies NIL -> T
T equivalent T -> T
T equivalent 0 -> 0
T equivalent NIL -> NIL
0 equivalent T -> 0
0 equivalent 0 -> 0
0 equivalent NIL -> 0
NIL equivalent T -> NIL
NIL equivalent 0 -> 0
NIL equivalent NIL -> T

PureBasic

Translation of: FreeBasic
DataSection
  TLogic:
  Data.i -1,0,1
  TSymb:
  Data.s "F","?","T"
EndDataSection

Structure TL
  F.i
  M.i
  T.i
EndStructure

Structure SYM
  TS.s{2}[3]
EndStructure

*L.TL=?TLogic
*S.SYM=?TSymb

Procedure.i NOT3(*x.TL)
  ProcedureReturn -*x
EndProcedure

Procedure.i AND3(*x.TL,*y.TL)
  If *x>*y : ProcedureReturn *y : Else : ProcedureReturn *x : EndIf
EndProcedure

Procedure.i OR3(*x.TL,*y.TL)
  If *x<*y : ProcedureReturn *y : Else : ProcedureReturn *x : EndIf
EndProcedure

Procedure.i EQV3(*x.TL,*y.TL)
  ProcedureReturn *x * *y
EndProcedure

Procedure.i IMP3(*x.TL,*y.TL)
  If -*y>*x : ProcedureReturn -*y : Else : ProcedureReturn *x : EndIf
EndProcedure

If OpenConsole("")
  PrintN("     (AND)      ( OR)      (EQV)      (IMP)     (NOT)")
  PrintN("     F ? T      F ? T      F ? T      F ? T          ")
  PrintN("    -------------------------------------------------")
  For *i.TL=*L\F To *L\T
    rs$=" "+*S\TS[*i+1]+" | "
    rs$+*S\TS[AND3(*L\F,*i)+1]+" "+*S\TS[AND3(*L\M,*i)+1]+" "+*S\TS[AND3(*L\T,*i)+1]
    rs$+"      "
    rs$+*S\TS[OR3(*L\F,*i)+1] +" "+*S\TS[OR3(*L\M,*i)+1] +" "+*S\TS[OR3(*L\T,*i)+1]
    rs$+"      "
    rs$+*S\TS[EQV3(*L\F,*i)+1]+" "+*S\TS[EQV3(*L\M,*i)+1]+" "+*S\TS[EQV3(*L\T,*i)+1]
    rs$+"      "
    rs$+*S\TS[IMP3(*L\F,*i)+1]+" "+*S\TS[IMP3(*L\M,*i)+1]+" "+*S\TS[IMP3(*L\T,*i)+1]
    rs$+"      "+*S\TS[NOT3(*i)+1]
    PrintN(rs$)
  Next
EndIf
Input()
Output:
     (AND)      ( OR)      (EQV)      (IMP)     (NOT)
     F ? T      F ? T      F ? T      F ? T          
    -------------------------------------------------
 F | F F F      F ? T      T ? F      T T T      T
 ? | F ? ?      ? ? T      ? ? ?      ? ? T      ?
 T | F ? T      T T T      F ? T      F ? T      F

Python

In Python, the keywords 'and', 'not', and 'or' are coerced to always work as boolean operators. I have therefore overloaded the boolean bitwise operators &, |, ^ to provide the required functionality.

class Trit(int):
    def __new__(cls, value):
        if value == 'TRUE':
            value = 1
        elif value == 'FALSE':
            value = 0
        elif value == 'MAYBE':
            value = -1
        return super(Trit, cls).__new__(cls, value // (abs(value) or 1)) 

    def __repr__(self):
        if self > 0:
            return 'TRUE'
        elif self == 0:
            return 'FALSE'
        return 'MAYBE'

    def __str__(self):
        return repr(self)

    def __bool__(self):
        if self > 0:
            return True
        elif self == 0:
            return False
        else:
            raise ValueError("invalid literal for bool(): '%s'" % self)

    def __or__(self, other):
        if isinstance(other, Trit):
            return _ttable[(self, other)][1]
        else:
            try:
                return _ttable[(self, Trit(bool(other)))][1]
            except:
                return NotImplemented

    def __ror__(self, other):
        if isinstance(other, Trit):
            return _ttable[(self, other)][1]
        else:
            try:
                return _ttable[(self, Trit(bool(other)))][1]
            except:
                return NotImplemented

    def __and__(self, other):
        if isinstance(other, Trit):
            return _ttable[(self, other)][0]
        else:
            try:
                return _ttable[(self, Trit(bool(other)))][0]
            except:
                return NotImplemented

    def __rand__(self, other):
        if isinstance(other, Trit):
            return _ttable[(self, other)][0]
        else:
            try:
                return _ttable[(self, Trit(bool(other)))][0]
            except:
                return NotImplemented

    def __xor__(self, other):
        if isinstance(other, Trit):
            return _ttable[(self, other)][2]
        else:
            try:
                return _ttable[(self, Trit(bool(other)))][2]
            except:
                return NotImplemented

    def __rxor__(self, other):
        if isinstance(other, Trit):
            return _ttable[(self, other)][2]
        else:
            try:
                return _ttable[(self, Trit(bool(other)))][2]
            except:
                return NotImplemented

    def __invert__(self):
        return _ttable[self]
    
    def __getattr__(self, name):
        if name in ('_n', 'flip'):
            # So you can do x._n == x.flip; the inverse of x
            # In Python 'not' is strictly boolean so we can't write `not x`
            # Same applies to keywords 'and' and 'or'.
            return _ttable[self]
        else:
            raise AttributeError 


        
TRUE, FALSE, MAYBE = Trit(1), Trit(0), Trit(-1)

_ttable = {
    #    A: -> flip_A
         TRUE: FALSE,
        FALSE:  TRUE,
        MAYBE: MAYBE,
    #     (A, B): -> (A_and_B, A_or_B, A_xor_B)
        (MAYBE, MAYBE): (MAYBE, MAYBE, MAYBE),
        (MAYBE, FALSE): (FALSE, MAYBE, MAYBE),
        (MAYBE,  TRUE): (MAYBE,  TRUE, MAYBE),
        (FALSE, MAYBE): (FALSE, MAYBE, MAYBE),
        (FALSE, FALSE): (FALSE, FALSE, FALSE),
        (FALSE,  TRUE): (FALSE,  TRUE,  TRUE),
        ( TRUE, MAYBE): (MAYBE,  TRUE, MAYBE),
        ( TRUE, FALSE): (FALSE,  TRUE,  TRUE),
        ( TRUE,  TRUE): ( TRUE,  TRUE, FALSE),
    }


values = ('FALSE', 'TRUE ', 'MAYBE')

print("\nTrit logical inverse, '~'")
for a in values:
    expr = '~%s' % a
    print('  %s = %s' % (expr, eval(expr)))

for op, ophelp in (('&', 'and'), ('|', 'or'), ('^', 'exclusive-or')):
    print("\nTrit logical %s, '%s'" % (ophelp, op))
    for a in values:
        for b in values:
            expr = '%s %s %s' % (a, op, b)
            print('  %s = %s' % (expr, eval(expr)))
Output:
Trit logical inverse, '~'
  ~FALSE = TRUE
  ~TRUE  = FALSE
  ~MAYBE = MAYBE

Trit logical and, '&'
  FALSE & FALSE = FALSE
  FALSE & TRUE  = FALSE
  FALSE & MAYBE = FALSE
  TRUE  & FALSE = FALSE
  TRUE  & TRUE  = TRUE
  TRUE  & MAYBE = MAYBE
  MAYBE & FALSE = FALSE
  MAYBE & TRUE  = MAYBE
  MAYBE & MAYBE = MAYBE

Trit logical or, '|'
  FALSE | FALSE = FALSE
  FALSE | TRUE  = TRUE
  FALSE | MAYBE = MAYBE
  TRUE  | FALSE = TRUE
  TRUE  | TRUE  = TRUE
  TRUE  | MAYBE = TRUE
  MAYBE | FALSE = MAYBE
  MAYBE | TRUE  = TRUE
  MAYBE | MAYBE = MAYBE

Trit logical exclusive-or, '^'
  FALSE ^ FALSE = FALSE
  FALSE ^ TRUE  = TRUE
  FALSE ^ MAYBE = MAYBE
  TRUE  ^ FALSE = TRUE
  TRUE  ^ TRUE  = FALSE
  TRUE  ^ MAYBE = MAYBE
  MAYBE ^ FALSE = MAYBE
  MAYBE ^ TRUE  = MAYBE
  MAYBE ^ MAYBE = MAYBE
Extra doodling in the Python shell
>>> values = (TRUE, FALSE, MAYBE)
>>> for a in values:
	for b in values:
		assert (a & ~b) | (b & ~a) == a ^ b

		
>>> 

Quackery

 
[ 2 ]                                       is maybe      (     --> t )
 
[ table 2 0 1 ]                             is jiggle     (   t --> n )
 
[ jiggle 1+ ]this[ swap peek do ]done[ ]    is 1-trit     (   t --> t )
 
[ swap jiggle 1+ ]this[ 
  swap peek 
  swap jiggle peek do ]done[ ]              is 2-trits    ( t t --> t )
 
[ 1-trit $ "true" $ "maybe" $ "false" ]     is trit$      (   t --> $ )
 
[ trit$ echo$ ]                             is echotrit   (   t -->   )
 
[ dup echotrit true = if sp ]               is paddedtrit (   t -->   )
 
          ( true  maybe false )
[ 1-trit    false maybe true  ]             is t.not      (   t --> t )
 
                    ( true  maybe false )
[ 2-trits ( true  ) [ false maybe true  ]
          ( maybe ) [ maybe maybe true  ]
          ( false ) [ true  true  true  ] ] is t.nand     ( t t --> t )
 
                    ( true  maybe false )
[ 2-trits ( true  ) [ true  maybe false ]
          ( maybe ) [ maybe maybe false ]
          ( false ) [ false false false ] ] is t.and      ( t t --> t )
 
                    ( true  maybe false )
[ 2-trits ( true  ) [ true  true  true  ]
          ( maybe ) [ true  maybe maybe ]
          ( false ) [ true  maybe false ] ] is t.or       ( t t --> t )
 
                    ( true  maybe false )
[ 2-trits ( true  ) [ false maybe true  ]
          ( maybe ) [ maybe maybe maybe ]
          ( false ) [ true  maybe false ] ] is t.xor      ( t t --> t )
 
( Quackery does not have operators for material implication 
  (if a then b, a implies b) or material equivalence 
  (a ≣ b, A <=> b). However, The Book of Quackery notes that they
  can be defined as:  
    "[ swap not or ] is implies ( a b --> c )"
    "[ xor not ]     is <=>     ( a b --> c )"
  so we will adapt these to test some of the ternary operators. )
 
[ swap t.not t.or ]                         is t.implies  ( t t --> t )
 
[ t.xor t.not ]                             is t.<=>      ( t t --> t )
 
cr
say " t.not     | true  | maybe | false"             cr
say " ---------------------------------"             cr
say "           | " true  t.not paddedtrit           sp
say            "| " maybe t.not paddedtrit           sp
say            "| " false t.not paddedtrit           cr
cr
say " t.and     | true  | maybe | false"             cr
say " ---------------------------------"             cr
say "   true    | " true  true  t.and paddedtrit     sp
say            "| " true  maybe t.and paddedtrit     sp
say            "| " true  false t.and paddedtrit     cr
say "   maybe   | " maybe true  t.and paddedtrit     sp
say            "| " maybe maybe t.and paddedtrit     sp
say            "| " maybe false t.and paddedtrit     cr
say "   false   | " false true  t.and paddedtrit     sp
say            "| " false maybe t.and paddedtrit     sp
say            "| " false false t.and paddedtrit     cr
cr
say " t.or      | true  | maybe | false"             cr
say " ---------------------------------"             cr
say "   true    | " true  true   t.or paddedtrit     sp
say            "| " true  maybe  t.or paddedtrit     sp
say            "| " true  false  t.or paddedtrit     cr
say "   maybe   | " maybe true   t.or paddedtrit     sp
say            "| " maybe maybe  t.or paddedtrit     sp
say            "| " maybe false  t.or paddedtrit     cr
say "   false   | " false true   t.or paddedtrit     sp
say            "| " false maybe  t.or paddedtrit     sp
say            "| " false false  t.or paddedtrit     cr
cr
say " t.implies | true  | maybe | false"             cr
say " ---------------------------------"             cr
say "   true    | " true  true  t.implies paddedtrit sp
say            "| " true  maybe t.implies paddedtrit sp
say            "| " true  false t.implies paddedtrit cr
say "   maybe   | " maybe true  t.implies paddedtrit sp
say            "| " maybe maybe t.implies paddedtrit sp
say            "| " maybe false t.implies paddedtrit cr
say "   false   | " false true  t.implies paddedtrit sp
say            "| " false maybe t.implies paddedtrit sp
say            "| " false false t.implies paddedtrit cr
cr
say " t.<=>     | true  | maybe | false"             cr
say " ---------------------------------"             cr
say "   true    | " true  true  t.<=> paddedtrit     sp
say            "| " true  maybe t.<=> paddedtrit     sp
say            "| " true  false t.<=> paddedtrit     cr
say "   maybe   | " maybe true  t.<=> paddedtrit     sp
say            "| " maybe maybe t.<=> paddedtrit     sp
say            "| " maybe false t.<=> paddedtrit     cr
say "   false   | " false true  t.<=> paddedtrit     sp
say            "| " false maybe t.<=> paddedtrit     sp
say            "| " false false t.<=> paddedtrit     cr

Output:

 t.not     | true  | maybe | false
 ---------------------------------
           | false | maybe | true 

 t.and     | true  | maybe | false
 ---------------------------------
   true    | true  | maybe | false
   maybe   | maybe | maybe | false
   false   | false | false | false

 t.or      | true  | maybe | false
 ---------------------------------
   true    | true  | true  | true 
   maybe   | true  | maybe | maybe
   false   | true  | maybe | false

 t.implies | true  | maybe | false
 ---------------------------------
   true    | true  | maybe | false
   maybe   | true  | maybe | maybe
   false   | true  | true  | true 

 t.<=>     | true  | maybe | false
 ---------------------------------
   true    | true  | maybe | false
   maybe   | maybe | maybe | maybe
   false   | false | maybe | true 

Racket

#lang typed/racket

; to avoid the hassle of adding a maybe value that is as special as
; the two standard booleans, we'll use symbols to make our own
(define-type trit (U 'true 'false 'maybe))

(: not (trit -> trit))
(define (not a)
  (case a
    [(true) 'false]
    [(maybe) 'maybe]
    [(false) 'true]))

(: and (trit trit -> trit))
(define (and a b)
  (case a
    [(false) 'false]
    [(maybe) (case b
               [(false) 'false]
               [else 'maybe])]
    [(true) (case b
              [(true) 'true]
              [(maybe) 'maybe]
              [(false) 'false])]))

(: or (trit trit -> trit))
(define (or a b)
  (case a
    [(true) 'true]
    [(maybe) (case b
               [(true) 'true]
               [else 'maybe])]
    [(false) (case b
               [(true) 'true]
               [(maybe) 'maybe]
               [(false) 'false])]))

(: ifthen (trit trit -> trit))
(define (ifthen a b)
  (case b
    [(true) 'true]
    [(maybe) (case a
               [(false) 'true]
               [else 'maybe])]
    [(false) (case a
               [(true) 'false]
               [(maybe) 'maybe]
               [(false) 'true])]))

(: iff (trit trit -> trit))
(define (iff a b)
  (case a
    [(maybe) 'maybe]
    [(true) b]
    [(false) (not b)]))

(for: : Void ([a (in-list '(true maybe false))])
      (printf "~a ~a = ~a~n" (object-name not) a (not a)))
(for: : Void ([proc (in-list (list and or ifthen iff))])
  (for*: : Void ([a (in-list '(true maybe false))]
                 [b (in-list '(true maybe false))])
         (printf "~a ~a ~a = ~a~n" a (object-name proc) b (proc a b))))
Output:
not true = false
not maybe = maybe
not false = true
true and true = true
true and maybe = maybe
true and false = false
maybe and true = maybe
maybe and maybe = maybe
maybe and false = false
false and true = false
false and maybe = false
false and false = false
true or true = true
true or maybe = true
true or false = true
maybe or true = true
maybe or maybe = maybe
maybe or false = maybe
false or true = true
false or maybe = maybe
false or false = false
true ifthen true = true
true ifthen maybe = maybe
true ifthen false = false
maybe ifthen true = true
maybe ifthen maybe = maybe
maybe ifthen false = maybe
false ifthen true = true
false ifthen maybe = true
false ifthen false = true
true iff true = true
true iff maybe = maybe
true iff false = false
maybe iff true = maybe
maybe iff maybe = maybe
maybe iff false = maybe
false iff true = false
false iff maybe = maybe
false iff false = true

Raku

(formerly Perl 6)

Works with: rakudo version 2018.03

The precedence of each operator is specified as equivalent to an existing operator. We've taken the liberty of using a double arrow for implication, to avoid confusing it with , (U+2283 SUPERSET OF).

# Implementation:
enum Trit <Foo Moo Too>;

sub prefix:<¬> (Trit $a) { Trit(1-($a-1)) }

sub infix:<∧> (Trit $a, Trit $b) is equiv(&infix:<*>) { $a min $b }
sub infix:<∨> (Trit $a, Trit $b) is equiv(&infix:<+>) { $a max $b }

sub infix:<⇒> (Trit $a, Trit $b) is equiv(&infix:<..>) { ¬$a max $b }
sub infix:<≡> (Trit $a, Trit $b) is equiv(&infix:<eq>) { Trit(1 + ($a-1) * ($b-1)) }

# Testing:
say '¬';
say "Too {¬Too}";
say "Moo {¬Moo}";
say "Foo {¬Foo}";

sub tbl (&op,$name) {
    say '';
    say "$name   Too Moo Foo";
    say "   ╔═══════════";
    say "Too║{op Too,Too} {op Too,Moo} {op Too,Foo}";
    say "Moo║{op Moo,Too} {op Moo,Moo} {op Moo,Foo}";
    say "Foo║{op Foo,Too} {op Foo,Moo} {op Foo,Foo}";
}

tbl(&infix:<∧>, '∧');
tbl(&infix:<∨>, '∨');
tbl(&infix:<⇒>, '⇒');
tbl(&infix:<≡>, '≡');

say '';
say 'Precedence tests should all print "Too":';
say ~(
    FooTooTooToo,
    Foo ∧ (TooToo) ≡ Foo,
    TooTooFooToo,
    (TooToo) ∧ FooFoo,

    ¬TooTooTooToo,
    ¬Too ∧ (TooToo) ≡ ¬Too,
    TooToo ∧ ¬TooToo,
    (TooToo) ∧ ¬Too ≡ ¬Too,
 
    FooTooFooFooToo,
    FooTooTooFooFoo,
);
Output:
¬
Too Foo
Moo Moo
Foo Too

∧   Too Moo Foo
   ╔═══════════
Too║Too Moo Foo
Moo║Moo Moo Foo
Foo║Foo Foo Foo

∨   Too Moo Foo
   ╔═══════════
Too║Too Too Too
Moo║Too Moo Moo
Foo║Too Moo Foo

⇒   Too Moo Foo
   ╔═══════════
Too║Too Moo Foo
Moo║Too Moo Moo
Foo║Too Too Too

≡   Too Moo Foo
   ╔═══════════
Too║Too Moo Foo
Moo║Moo Moo Moo
Foo║Foo Moo Too

Precedence tests should all print "Too":
Too Too Too Too Too Too Too Too Too Too

Red

Works with: Red version 0.6.4
Red ["Ternary logic"]

		; define trits as a set of 3 Red words: 'oui, 'non and 'bof
		; ('bof is a French teenager word expressing indifference)
trits: [oui bof non]

		; set the value of each word to itself
		; so the expression " oui " will evaluate to word 'oui
foreach t trits [set t to-lit-word t]

		; utility function to test if a word is a trit
trit?: function [t] [not none? find trits t]

		; ------ prefix operators ------
		; unary operator
tnot: !: function [a][
	select [oui non oui bof bof] a
]
		; binary (prefix) operators
tand: function [a b][
	either all [a = oui b = oui][oui][
		either any [a = non b = non][non][bof]
]]
tor: function [a b][
	either any [a = oui b = oui][oui][
		either all [a = non b = non][non][bof]
]]
timp: function [a b][
	either a = oui [b][
		either a = non [oui][
			either b = oui [oui][bof]
]]]
teq: function [a b][
	either any [a = bof b = bof][bof][
		either a = b [oui][non]
]]
		; ------ infix operators ------
&:   make op! :tand
|:   make op! :tor
=>:  make op! :timp
<=>: make op! :teq

		; some examples
probe init: [
a: oui
b: bof
c: non]
do init
foreach s [[! (! a)] [a & b] [a | (b & (oui | non))] 
		[! ((a | b) | b & c)] [(a & b) | c]][
	print rejoin [pad mold s 25 " " do s]
]
Output:
[
    a: oui 
    b: bof 
    c: non
]
[! (! a)]                 oui
[a & b]                   bof
[a | (b & (oui | non))]   oui
[! ((a | b) | b & c)]     oui
[(a & b) | c]             bof

REXX

This REXX program is a re-worked version of the REXX program used for the Rosetta Code task:   truth table.

/*REXX program displays a ternary truth table  [true, false, maybe]   for the variables */
/*──── and one or more expressions.                                                     */
/*──── Infix notation is supported with one character propositional constants.          */
/*──── Variables (propositional constants) allowed:    A ──► Z,     a ──► z   except  u.*/
/*──── All propositional constants are case insensative  (except lowercase  v).         */
parse arg $express                               /*obtain optional argument from the CL.*/
if $express\=''  then do                         /*Got one?  Then show user's expression*/
                      call truthTable $express   /*display the user's truth table──►term*/
                      exit                       /*we're all done with the truth table. */
                      end

call truthTable  "a & b ; AND"
call truthTable  "a | b ; OR"
call truthTable  "a ^ b ; XOR"
call truthTable  "a ! b ; NOR"
call truthTable  "a ¡ b ; NAND"
call truthTable  "a xnor b ; XNOR"               /*XNOR  is the same as  NXOR.          */
exit                                             /*stick a fork in it,  we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
truthTable: procedure; parse arg $ ';' comm 1 $o;        $o=strip($o)
      $=translate(strip($), '|', "v");                   $u=$;        upper $u
      $u=translate($u, '()()()', "[]{}«»");              $$.=0;       PCs=;        hdrPCs=
      @abc= 'abcdefghijklmnopqrstuvwxyz';                @abcU=@abc;  upper @abcU
      @= 'ff'x                                         /*─────────infix operators───────*/
      op.=                                             /*a single quote (') wasn't      */
                                                       /*     implemented for negation. */
      op.0  = 'false  boolFALSE'                       /*unconditionally  FALSE         */
      op.1  = 'and    and & *'                         /* AND, conjunction              */
      op.2  = 'naimpb NaIMPb'                          /*not A implies B                */
      op.3  = 'boolb  boolB'                           /*B  (value of)                  */
      op.4  = 'nbimpa NbIMPa'                          /*not B implies A                */
      op.5  = 'boola  boolA'                           /*A  (value of)                  */
      op.6  = 'xor    xor && % ^'                      /* XOR, exclusive OR             */
      op.7  = 'or     or | + v'                        /*  OR, disjunction              */
      op.8  = 'nor    nor ! ↓'                         /* NOR, not OR, Pierce operator  */
      op.9  = 'xnor   xnor nxor'                       /*NXOR, not exclusive OR, not XOR*/
      op.10 = 'notb   notB'                            /*not B (value of)               */
      op.11 = 'bimpa  bIMPa'                           /*    B implies A                */
      op.12 = 'nota   notA'                            /*not A (value of)               */
      op.13 = 'aimpb  aIMPb'                           /*    A implies B                */
      op.14 = 'nand   nand ¡ ↑'                        /*NAND, not AND, Sheffer operator*/
      op.15 = 'true   boolTRUE'                        /*unconditionally   TRUE         */
                                                       /*alphabetic names need changing.*/
      op.16 = '\   NOT ~ ─ . ¬'                        /* NOT, negation                 */
      op.17 = '>   GT'                                 /*conditional greater than       */
      op.18 = '>=  GE ─> => ──> ==>' "1a"x             /*conditional greater than or eq.*/
      op.19 = '<   LT'                                 /*conditional less than          */
      op.20 = '<=  LE <─ <= <── <=='                   /*conditional less then or equal */
      op.21 = '\=  NE ~= ─= .= ¬='                     /*conditional not equal to       */
      op.22 = '=   EQ EQUAL EQUALS =' "1b"x            /*biconditional  (equals)        */
      op.23 = '0   boolTRUE'                           /*TRUEness                       */
      op.24 = '1   boolFALSE'                          /*FALSEness                      */

      op.25 = 'NOT NOT NEG'                            /*not, neg  (negative)           */

        do jj=0  while  op.jj\=='' | jj<16             /*change opers──►what REXX likes.*/
        new=word(op.jj,1)
          do kk=2  to words(op.jj)                     /*handle each token separately.  */
          _=word(op.jj, kk);     upper _
          if wordpos(_, $u)==0   then iterate          /*no such animal in this string. */
          if datatype(new, 'm')  then new!=@           /*expresion needs transcribing.  */
                                 else new!=new
          $u=changestr(_, $u, new!)                    /*transcribe the function (maybe)*/
          if new!==@  then $u=changeFunc($u, @, new)   /*use the internal boolean name. */
          end   /*kk*/
        end     /*jj*/

      $u=translate($u, '()', "{}")                     /*finish cleaning up transcribing*/
            do jj=1  for length(@abcU)                 /*see what variables are used.   */
            _=substr(@abcU, jj, 1)                     /*use available upercase alphabet*/
            if pos(_,$u)==0  then iterate              /*found one?   No, keep looking. */
            $$.jj=2                                    /*found:  set upper bound for it.*/
            PCs=PCs _                                  /*also, add to propositional cons*/
            hdrPCs=hdrPCS  center(_, length('false'))  /*build a propositional cons hdr.*/
            end   /*jj*/
      $u=PCs  '('$u")"                                 /*sep prop. cons. from expression*/
      ptr='_────►_'                                    /*a pointer for the truth table. */
      hdrPCs=substr(hdrPCs,2)                          /*create a header for prop. cons.*/
      say hdrPCs left('', length(ptr) -1)   $o         /*show prop cons hdr +expression.*/
      say copies('───── ', words(PCs))   left('', length(ptr)-2)   copies('─', length($o))
                                                       /*Note: "true"s:  right─justified*/
              do a=0  to $$.1
               do b=0  to $$.2
                do c=0  to $$.3
                 do d=0  to $$.4
                  do e=0  to $$.5
                   do f=0  to $$.6
                    do g=0  to $$.7
                     do h=0  to $$.8
                      do i=0  to $$.9
                       do j=0  to $$.10
                        do k=0  to $$.11
                         do l=0  to $$.12
                          do m=0  to $$.13
                           do n=0  to $$.14
                            do o=0  to $$.15
                             do p=0  to $$.16
                              do q=0  to $$.17
                               do r=0  to $$.18
                                do s=0  to $$.19
                                 do t=0  to $$.20
                                  do u=0  to $$.21
                                   do !=0  to $$.22
                                    do w=0  to $$.23
                                     do x=0  to $$.24
                                      do y=0  to $$.25
                                       do z=0  to $$.26
                                       interpret '_=' $u             /*evaluate truth T.*/
                                       _=changestr(0, _, 'false')    /*convert 0──►false*/
                                       _=changestr(1, _, '_true')    /*convert 1──►_true*/
                                       _=changestr(2, _, 'maybe')    /*convert 2──►maybe*/
                                       _=insert(ptr, _, wordindex(_, words(_)) -1) /*──►*/
                                       say translate(_, , '_')       /*display truth tab*/
                                       end   /*z*/
                                      end    /*y*/
                                     end     /*x*/
                                    end      /*w*/
                                   end       /*v*/
                                  end        /*u*/
                                 end         /*t*/
                                end          /*s*/
                               end           /*r*/
                              end            /*q*/
                             end             /*p*/
                            end              /*o*/
                           end               /*n*/
                          end                /*m*/
                         end                 /*l*/
                        end                  /*k*/
                       end                   /*j*/
                      end                    /*i*/
                     end                     /*h*/
                    end                      /*g*/
                   end                       /*f*/
                  end                        /*e*/
                 end                         /*d*/
                end                          /*c*/
               end                           /*b*/
              end                            /*a*/
      say
      return
/*──────────────────────────────────────────────────────────────────────────────────────*/
scan: procedure; parse arg x,at;    L=length(x);    t=L;     lp=0;     apost=0;    quote=0
      if at<0  then do;   t=1;   x= translate(x, '()', ")(");    end

                      do j=abs(at)  to t  by sign(at);  _=substr(x,j,1);  __=substr(x,j,2)
                      if quote           then do; if _\=='"'  then iterate
                                              if __=='""'     then do; j=j+1; iterate; end
                                              quote=0;  iterate
                                              end
                      if apost           then do; if _\=="'"  then iterate
                                              if __=="''"     then do; j=j+1; iterate; end
                                              apost=0;  iterate
                                              end
                      if _=='"'          then do;  quote=1;                   iterate; end
                      if _=="'"          then do;  apost=1;                   iterate; end
                      if _==' '          then iterate
                      if _=='('          then do;  lp=lp+1;                   iterate; end
                      if lp\==0          then do;  if _==')'  then lp=lp-1;   iterate; end
                      if datatype(_,'U') then return j - (at<0)
                      if at<0            then return j + 1
                      end   /*j*/
      return min(j,L)
/*──────────────────────────────────────────────────────────────────────────────────────*/
changeFunc: procedure;  parse arg z,fC,newF;       funcPos= 0
            do forever
            funcPos= pos(fC, z, funcPos + 1);      if funcPos==0  then return z
            origPos= funcPos
                  z= changestr(fC, z, ",'"newF"',")
            funcPos= funcPos + length(newF) + 4
              where= scan(z, funcPos)     ;        z= insert(    '}',  z,  where)
              where= scan(z, 1 - origPos) ;        z= insert('trit{',  z,  where)
            end   /*forever*/
/*──────────────────────────────────────────────────────────────────────────────────────*/
trit: procedure; arg a,$,b;   v= \(a==2 | b==2);    o= (a==1 | b==1);     z= (a==0 | b==0)
            select
            when $=='FALSE'   then            return 0
            when $=='AND'     then if v  then return a & b;      else return 2
            when $=='NAIMPB'  then if v  then return \(\a & \b); else return 2
            when $=='BOOLB'   then            return b
            when $=='NBIMPA'  then if v  then return \(\b & \a); else return 2
            when $=='BOOLA'   then            return a
            when $=='XOR'     then if v  then return a && b    ; else return 2
            when $=='OR'      then if v  then return a | b     ; else  if o  then return 1
                                                                             else return 2
            when $=='NOR'     then if v  then return \(a | b)  ; else return 2
            when $=='XNOR'    then if v  then return \(a && b) ; else return 2
            when $=='NOTB'    then if v  then return \b        ; else return 2
            when $=='NOTA'    then if v  then return \a        ; else return 2
            when $=='AIMPB'   then if v  then return \(a & \b) ; else return 2
            when $=='NAND'    then if v  then return \(a &  b) ; else  if z  then return 1
                                                                             else return 2
            when $=='TRUE'    then           return   1
            otherwise                        return -13       /*error, unknown function.*/
            end   /*select*/

Some older REXXes don't have a   changestr   BIF, so one is included here   ──►   CHANGESTR.REX.

output

  A     B          a & b ; AND
───── ─────        ───────────
false false  ────► false
false  true  ────► false
false maybe  ────► maybe
 true false  ────► false
 true  true  ────►  true
 true maybe  ────► maybe
maybe false  ────► maybe
maybe  true  ────► maybe
maybe maybe  ────► maybe

  A     B          a | b ; OR
───── ─────        ──────────
false false  ────► false
false  true  ────►  true
false maybe  ────► maybe
 true false  ────►  true
 true  true  ────►  true
 true maybe  ────►  true
maybe false  ────► maybe
maybe  true  ────►  true
maybe maybe  ────► maybe

  A     B          a ^ b ; XOR
───── ─────        ───────────
false false  ────► false
false  true  ────►  true
false maybe  ────► maybe
 true false  ────►  true
 true  true  ────► false
 true maybe  ────► maybe
maybe false  ────► maybe
maybe  true  ────► maybe
maybe maybe  ────► maybe

  A     B          a ! b ; NOR
───── ─────        ───────────
false false  ────►  true
false  true  ────► false
false maybe  ────► maybe
 true false  ────► false
 true  true  ────► false
 true maybe  ────► maybe
maybe false  ────► maybe
maybe  true  ────► maybe
maybe maybe  ────► maybe

  A     B          a ¡ b ; NAND
───── ─────        ────────────
false false  ────►  true
false  true  ────►  true
false maybe  ────►  true
 true false  ────►  true
 true  true  ────► false
 true maybe  ────► maybe
maybe false  ────►  true
maybe  true  ────► maybe
maybe maybe  ────► maybe

  A     B          a xnor b ; XNOR
───── ─────        ───────────────
false false  ────►  true
false  true  ────► false
false maybe  ────► maybe
 true false  ────► false
 true  true  ────►  true
 true maybe  ────► maybe
maybe false  ────► maybe
maybe  true  ────► maybe
maybe maybe  ────► maybe

RPL

Ternary logic can be considered as a simplified version of Zadeh's fuzzy logic. In this paradigm, boolean values turn into floating-point ones going from 0 (completely false) to 1 (completely true): AND(a,b), OR(a,b) and NOT(a) are resp. redefined as MIN(a,b), MAX(a,b) and (1-a). Other boolean operators can then be built by combining these 3 atoms. A specific word is also needed to display results as ternary constants instead of numbers.

Works with: Halcyon Calc version 4.2.7
≪ 2 * CEIL 1 + { ‘FALSE’ ‘MAYBE’ ‘TRUE’ } SWAP GET ≫ 'TELL’ STO
≪ OR EVAL TELL ≫ 'TAND’ STO
≪ AND EVAL TELL ≫ 'TOR’ STO
≪ 1 SWAP - EVAL TELL ≫ 'TNOT’ STO
≪ SWAP TNOT TOR EVAL TELL ≫ 'TIMPLY’ STO
≪ DUP2 TNOT TAND ROT TNOT ROT TAND TOR EVAL TELL ≫ 'TXOR’ STO
1 'TRUE' STO 0.5 'MAYBE' STO 0 'FALSE' STO
FALSE MAYBE TXOR 
1: ‘TRUE’

Only the Soviets could understand such a logic...

Ruby

Ruby, like Smalltalk, has two boolean classes: TrueClass for true and FalseClass for false. We add a third class, MaybeClass for MAYBE, and define ternary logic for all three classes.

We keep !a, a & b and so on for binary logic. We add !a.trit, a.trit & b and so on for ternary logic. The code for !a.trit uses def !, which works with Ruby 1.9, but fails as a syntax error with Ruby 1.8.

Works with: Ruby version 1.9
# trit.rb - ternary logic
# http://rosettacode.org/wiki/Ternary_logic

require 'singleton'

# MAYBE, the only instance of MaybeClass, enables a system of ternary
# logic using TrueClass#trit, MaybeClass#trit and FalseClass#trit.
#
#  !a.trit      # ternary not
#  a.trit & b   # ternary and
#  a.trit | b   # ternary or
#  a.trit ^ b   # ternary exclusive or
#  a.trit == b  # ternary equal
#
# Though +true+ and +false+ are internal Ruby values, +MAYBE+ is not.
# Programs may want to assign +maybe = MAYBE+ in scopes that use
# ternary logic. Then programs can use +true+, +maybe+ and +false+.
class MaybeClass
  include Singleton

  #  maybe.to_s  # => "maybe"
  def to_s; "maybe"; end
end

MAYBE = MaybeClass.instance

class TrueClass
  TritMagic = Object.new
  class << TritMagic
    def index; 0; end
    def !; false; end
    def & other; other; end
    def | other; true; end
    def ^ other; [false, MAYBE, true][other.trit.index]; end
    def == other; other; end
  end

  # Performs ternary logic. See MaybeClass.
  #  !true.trit        # => false
  #  true.trit & obj   # => obj
  #  true.trit | obj   # => true
  #  true.trit ^ obj   # => false, maybe or true
  #  true.trit == obj  # => obj
  def trit; TritMagic; end
end

class MaybeClass
  TritMagic = Object.new
  class << TritMagic
    def index; 1; end
    def !; MAYBE; end
    def & other; [MAYBE, MAYBE, false][other.trit.index]; end
    def | other; [true, MAYBE, MAYBE][other.trit.index]; end
    def ^ other; MAYBE; end
    def == other; MAYBE; end
  end

  # Performs ternary logic. See MaybeClass.
  #  !maybe.trit        # => maybe
  #  maybe.trit & obj   # => maybe or false
  #  maybe.trit | obj   # => true or maybe
  #  maybe.trit ^ obj   # => maybe
  #  maybe.trit == obj  # => maybe
  def trit; TritMagic; end
end

class FalseClass
  TritMagic = Object.new
  class << TritMagic
    def index; 2; end
    def !; true; end
    def & other; false; end
    def | other; other; end
    def ^ other; other; end
    def == other; [false, MAYBE, true][other.trit.index]; end
  end

  # Performs ternary logic. See MaybeClass.
  #  !false.trit        # => true
  #  false.trit & obj   # => false
  #  false.trit | obj   # => obj
  #  false.trit ^ obj   # => obj
  #  false.trit == obj  # => false, maybe or true
  def trit; TritMagic; end
end

This IRB session shows ternary not, and, or, equal.

$ irb
irb(main):001:0> require './trit'
=> true
irb(main):002:0> maybe = MAYBE
=> maybe
irb(main):003:0> !true.trit       
=> false
irb(main):004:0> !maybe.trit
=> maybe
irb(main):005:0> maybe.trit & false
=> false
irb(main):006:0> maybe.trit | true
=> true
irb(main):007:0> false.trit == true       
=> false
irb(main):008:0> false.trit == maybe
=> maybe

This program shows all 9 outcomes from a.trit ^ b.

require 'trit'
maybe = MAYBE

[true, maybe, false].each do |a|
  [true, maybe, false].each do |b|
    printf "%5s ^ %5s => %5s\n", a, b, a.trit ^ b
  end
end
$ ruby -I. trit-xor.rb
 true ^  true => false
 true ^ maybe => maybe
 true ^ false =>  true
maybe ^  true => maybe
maybe ^ maybe => maybe
maybe ^ false => maybe
false ^  true =>  true
false ^ maybe => maybe
false ^ false => false

Run BASIC

testFalse	= 0  ' F
testDoNotKnow	= 1  ' ?
testTrue	= 2  ' T
 
print "Short and long names for ternary logic values"
for i = testFalse to testTrue
    print shortName3$(i);" ";longName3$(i)
next i
print
 
print "Single parameter functions"
print "x";" ";"=x";"  ";"not(x)"
for i = testFalse to testTrue
    print shortName3$(i);"  ";shortName3$(i);"    ";shortName3$(not3(i))
next
print
 
print "Double  parameter fuctions"
html "<table border=1><TR align=center bgcolor=wheat><TD>x</td><td>y</td><td>x AND y</td><td>x OR y</td><td>x EQ y</td><td>x XOR y</td></tr>"
for a	= testFalse to testTrue
    for b	= testFalse to testTrue
      html "<TR align=center><td>"
      html shortName3$(a);        "</td><td>";shortName3$(b);        "</td><td>"
      html shortName3$(and3(a,b));"</td><td>";shortName3$(or3(a,b)); "</td><td>"
      html shortName3$(eq3(a,b)); "</td><td>";shortName3$(xor3(a,b));"</td></tr>"
    next
next
html "</table>"
function and3(a,b)
    and3	= min(a,b)
end function
 
function or3(a,b)
    or3	= max(a,b)
end function
 
function eq3(a,b)
    eq3 	= testFalse
    if a	= tDontKnow or b	= tDontKnow then eq3	= tDontKnow
    if a	= b then eq3	= testTrue
end function
 
function xor3(a,b)
    xor3	= not3(eq3(a,b))
end function
 
function not3(b)
    not3	= 2-b
end function
 
'------------------------------------------------
function shortName3$(i)
   shortName3$	= word$("F ? T", i+1)
end function
 
function longName3$(i)
    longName3$	= word$("False,Don't know,True", i+1, ",")
end function
Short and long names for ternary logic values
F False
? Don't know
T True

Single parameter functions
x =x  not(x)
F  F  T
?  ?  ?
T  T  F

Double  parameter fuctions
xyx AND yx OR yx EQ yx XOR y
FFFFTF
F?F?FT
FTFTFT
?FF?FT
????TF
?T?TFT
TFFTFT
T??TFT
TTTTTF

Rust

Translation of: Kotlin
use std::{ops, fmt};

#[derive(Copy, Clone, Debug)]
enum Trit {
    True,
    Maybe,
    False,
}

impl ops::Not for Trit {
    type Output = Self;
    fn not(self) -> Self {
        match self {
            Trit::True => Trit::False,
            Trit::Maybe => Trit::Maybe,
            Trit::False => Trit::True,
        }
    }
}

impl ops::BitAnd for Trit {
    type Output = Self;
    fn bitand(self, other: Self) -> Self {
        match (self, other) {
            (Trit::True, Trit::True) => Trit::True,
            (Trit::False, _) | (_, Trit::False) => Trit::False,
            _ => Trit::Maybe,
        }
    }
}

impl ops::BitOr for Trit {
    type Output = Self;
    fn bitor(self, other: Self) -> Self {
        match (self, other) {
            (Trit::True, _) | (_, Trit::True) => Trit::True,
            (Trit::False, Trit::False) => Trit::False,
            _ => Trit::Maybe,
        }
    }
}

impl Trit {
    fn imp(self, other: Self) -> Self {
        match self {
            Trit::True => other,
            Trit::Maybe => {
                if let Trit::True = other {
                    Trit::True
                } else {
                    Trit::Maybe
                }
            }
            Trit::False => Trit::True,
        }
    }

    fn eqv(self, other: Self) -> Self {
        match self {
            Trit::True => other,
            Trit::Maybe => Trit::Maybe,
            Trit::False => !other,
        }
    }
}

impl fmt::Display for Trit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "{}",
            match self {
                Trit::True => 'T',
                Trit::Maybe => 'M',
                Trit::False => 'F',
            }
        )
    }
}

static TRITS: [Trit; 3] = [Trit::True, Trit::Maybe, Trit::False];

fn main() {
    println!("not");
    println!("-------");
    for &t in &TRITS {
        println!(" {}  | {}", t, !t);
    }
    
    table("and", |a, b| a & b);
    table("or", |a, b| a | b);
    table("imp", |a, b| a.imp(b));
    table("eqv", |a, b| a.eqv(b));
}

fn table(title: &str, f: impl Fn(Trit, Trit) -> Trit) {
    println!();
    println!("{:3} | T  M  F", title);
    println!("-------------");
    for &t1 in &TRITS {
        print!(" {}  | ", t1);
        for &t2 in &TRITS {
            print!("{}  ", f(t1, t2));
        }
        println!();
    }
}
Output:
not
-------
 T  | F
 M  | M
 F  | T

and | T  M  F
-------------
 T  | T  M  F  
 M  | M  M  F  
 F  | F  F  F  

or  | T  M  F
-------------
 T  | T  T  T  
 M  | T  M  M  
 F  | T  M  F  

imp | T  M  F
-------------
 T  | T  M  F  
 M  | T  M  M  
 F  | T  T  T  

eqv | T  M  F
-------------
 T  | T  M  F  
 M  | M  M  M  
 F  | F  M  T  

Scala

sealed trait Trit { self =>
  def nand(that:Trit):Trit=(this,that) match {
    case (TFalse, _) => TTrue
    case (_, TFalse) => TTrue
    case (TMaybe, _) => TMaybe
    case (_, TMaybe) => TMaybe
    case _ => TFalse
  }
		
  def nor(that:Trit):Trit = this.or(that).not()
  def and(that:Trit):Trit = this.nand(that).not()
  def or(that:Trit):Trit = this.not().nand(that.not())
  def not():Trit = this.nand(this)
  def imply(that:Trit):Trit = this.nand(that.not())
  def equiv(that:Trit):Trit = this.and(that).or(this.nor(that))
}
case object TTrue extends Trit
case object TMaybe extends Trit
case object TFalse extends Trit

object TernaryLogic extends App {
  val v=List(TTrue, TMaybe, TFalse)
  println("- NOT -")
  for(a<-v) println("%6s => %6s".format(a, a.not))
  println("\n- AND -")
  for(a<-v; b<-v) println("%6s : %6s => %6s".format(a, b, a and b))
  println("\n- OR -")
  for(a<-v; b<-v) println("%6s : %6s => %6s".format(a, b, a or b))
  println("\n- Imply -")
  for(a<-v; b<-v) println("%6s : %6s => %6s".format(a, b, a imply b))
  println("\n- Equiv -")
  for(a<-v; b<-v) println("%6s : %6s => %6s".format(a, b, a equiv b))		
}
Output:
- NOT -
 TTrue => TFalse
TMaybe => TMaybe
TFalse =>  TTrue

- AND -
 TTrue :  TTrue =>  TTrue
 TTrue : TMaybe => TMaybe
 TTrue : TFalse => TFalse
TMaybe :  TTrue => TMaybe
TMaybe : TMaybe => TMaybe
TMaybe : TFalse => TFalse
TFalse :  TTrue => TFalse
TFalse : TMaybe => TFalse
TFalse : TFalse => TFalse

- OR -
 TTrue :  TTrue =>  TTrue
 TTrue : TMaybe =>  TTrue
 TTrue : TFalse =>  TTrue
TMaybe :  TTrue =>  TTrue
TMaybe : TMaybe => TMaybe
TMaybe : TFalse => TMaybe
TFalse :  TTrue =>  TTrue
TFalse : TMaybe => TMaybe
TFalse : TFalse => TFalse

- Imply -
 TTrue :  TTrue =>  TTrue
 TTrue : TMaybe => TMaybe
 TTrue : TFalse => TFalse
TMaybe :  TTrue =>  TTrue
TMaybe : TMaybe => TMaybe
TMaybe : TFalse => TMaybe
TFalse :  TTrue =>  TTrue
TFalse : TMaybe =>  TTrue
TFalse : TFalse =>  TTrue

- Equiv -
 TTrue :  TTrue =>  TTrue
 TTrue : TMaybe => TMaybe
 TTrue : TFalse => TFalse
TMaybe :  TTrue => TMaybe
TMaybe : TMaybe => TMaybe
TMaybe : TFalse => TMaybe
TFalse :  TTrue => TFalse
TFalse : TMaybe => TMaybe
TFalse : TFalse =>  TTrue

Seed7

The type boolean does not define separate xor, implies and equiv operators. But there are replacements for them:

Instead of Use
p xor q p <> q
p implies q p <= q
p equiv q p = q

Since ternary logic needs xor, implies and equiv with a trit result they are introduced as the operators xor, -> and ==. The trit operators and and or are defined as short circuit operators. A short circuit operator evaluates the second parameter only when necessary. This is analogous to the boolean operators and and or, which use also short circuit evaluation.

$ include "seed7_05.s7i";

const type: trit is new enum
    False, Maybe, True
  end enum;

# Enum types define comparisons (=, <, >, <=, >=, <>) and
# the conversions ord and conv.

const func string: str (in trit: aTrit) is
  return [] ("False", "Maybe", "True")[succ(ord(aTrit))];

enable_output(trit);  # Allow writing trit values

const array trit: tritNot is [] (True, Maybe, False);
const array array trit: tritAnd is [] (
    [] (False, False, False),
    [] (False, Maybe, Maybe),
    [] (False, Maybe, True ));
const array array trit: tritOr is [] (
    [] (False, Maybe, True ),
    [] (Maybe, Maybe, True ),
    [] (True,  True,  True ));
const array array trit: tritXor is [] (
    [] (False, Maybe, True ),
    [] (Maybe, Maybe, Maybe),
    [] (True,  Maybe, False));
const array array trit: tritImplies is [] (
    [] (True,  True,  True ),
    [] (Maybe, Maybe, True ),
    [] (False, Maybe, True ));
const array array trit: tritEquiv is [] (
    [] (True,  Maybe, False),
    [] (Maybe, Maybe, Maybe),
    [] (False, Maybe, True ));

const func trit: not (in trit: aTrit) is
  return tritNot[succ(ord(aTrit))];

const func trit: (in trit: aTrit1) and (in trit: aTrit2) is
  return tritAnd[succ(ord(aTrit1))][succ(ord(aTrit2))];
 
const func trit: (in trit: aTrit1) and (ref func trit: aTrit2) is func
  result
    var trit: res is False;
  begin
    if aTrit1 = True then
      res := aTrit2;
    elsif aTrit1 = Maybe and aTrit2 <> False then
      res := Maybe;
    end if;
  end func;

const func trit: (in trit: aTrit1) or (in trit: aTrit2) is
  return tritOr[succ(ord(aTrit1))][succ(ord(aTrit2))];
 
const func trit: (in trit: aTrit1) or (ref func trit: aTrit2) is func
  result
    var trit: res is True;
  begin
    if aTrit1 = False then
      res := aTrit2;
    elsif aTrit1 = Maybe and aTrit2 <> True then
      res := Maybe;
    end if;
  end func;

$ syntax expr: .().xor.() is -> 15;
const func trit: (in trit: aTrit1) xor (in trit: aTrit2) is
  return tritImplies[succ(ord(aTrit1))][succ(ord(aTrit2))];

const func trit: (in trit: aTrit1) -> (in trit: aTrit2) is
  return tritImplies[succ(ord(aTrit1))][succ(ord(aTrit2))];

const func trit: (in trit: aTrit1) == (in trit: aTrit2) is
  return tritEquiv[succ(ord(aTrit1))][succ(ord(aTrit2))];

const func trit: rand (in trit: low, in trit: high) is
  return trit conv (rand(ord(low), ord(high)));

# Begin of test code

var trit: operand1 is False;
var trit: operand2 is False;

const proc: writeTable (ref func trit: tritExpr, in string: name) is func
  begin
    writeln;
    writeln(" " <& name rpad 7 <& " | False  Maybe  True");
    writeln("---------+---------------------");
    for operand1 range False to True do
      write(" " <& operand1 rpad 7 <& " | ");
      for operand2 range False to True do
        write(tritExpr rpad 7);
      end for;
      writeln;
    end for;
  end func;

const proc: main is func
  begin
    writeln(" not" rpad 8 <& " | False  Maybe  True");
    writeln("---------+---------------------");
    write("         | ");
    for operand1 range False to True do
      write(not operand1 rpad 7);
    end for;
    writeln;
    writeTable(operand1 and operand2, "and");
    writeTable(operand1 or operand2,  "or");
    writeTable(operand1 xor operand2, "xor");
    writeTable(operand1 -> operand2,  "->");
    writeTable(operand1 == operand2,  "==");
  end func;
Output:
 not     | False  Maybe  True
---------+---------------------
         | True   Maybe  False  

 and     | False  Maybe  True
---------+---------------------
 False   | False  False  False  
 Maybe   | False  Maybe  Maybe  
 True    | False  Maybe  True   

 or      | False  Maybe  True
---------+---------------------
 False   | False  Maybe  True   
 Maybe   | Maybe  Maybe  True   
 True    | True   True   True   

 xor     | False  Maybe  True
---------+---------------------
 False   | True   True   True   
 Maybe   | Maybe  Maybe  True   
 True    | False  Maybe  True   

 ->      | False  Maybe  True
---------+---------------------
 False   | True   True   True   
 Maybe   | Maybe  Maybe  True   
 True    | False  Maybe  True   

 ==      | False  Maybe  True
---------+---------------------
 False   | True   Maybe  False  
 Maybe   | Maybe  Maybe  Maybe  
 True    | False  Maybe  True   

SparForte

As a structured script.

#!/usr/local/bin/spar
pragma annotate( summary, "ternary_logic" )
       @( description, "In logic, a three-valued logic (also trivalent, " )
       @( description, "ternary, or trinary logic, sometimes abbreviated " )
       @( description, "3VL) is any of several many-valued logic systems " )
       @( description, "in which there are three truth values indicating " )
       @( description, "true, false and some indeterminate third value. " )
       @( see_also, "http://rosettacode.org/wiki/Ternary_logic" );
pragma annotate( author, "Ken O. Burtch" );
pragma license( unrestricted );

pragma restriction( no_external_commands );

procedure ternary_logic is

type ternary is (no, maybe, yes);

function ternary_and( left : ternary; right : ternary ) return ternary is
begin
  if left < right then
     return left;
  else
     return right;
  end if;
end ternary_and;

function ternary_or( left : ternary; right : ternary ) return ternary is
begin
  if left > right then
     return left;
  else
     return right;
  end if;
end ternary_or;

function ternary_not( right : ternary ) return ternary is
begin
  case right is
  when yes => return no;
  when maybe => return maybe;
  when no => return yes;
  when others => put_line( "Unexpected value" );
  end case;
end ternary_not;

function ternary_image( ternary_value : ternary ) return string is
begin
  case ternary_value is
  when yes    => return "Yes";
  when no     => return "No";
  when maybe  => return "Maybe";
  when others => put_line( "Unexpected value" );
  end case;
end ternary_image;

begin
  ? "Ternary Not:"
  @ "not no    => " & ternary_image( ternary_not( no ) )
  @ "not maybe => " & ternary_image( ternary_not( maybe ) )
  @ "not yes   => " & ternary_image( ternary_not( yes ) );
  new_line;

  ? "Ternary And:"
  @ "no    and no    => " & ternary_image( ternary_and( no, no ) )
  @ "no    and maybe => " & ternary_image( ternary_and( no, maybe ) )
  @ "no    and yes   => " & ternary_image( ternary_and( no, yes ) )
  @ "maybe and no    => " & ternary_image( ternary_and( maybe, no ) )
  @ "maybe and maybe => " & ternary_image( ternary_and( maybe, maybe ) )
  @ "maybe and yes   => " & ternary_image( ternary_and( maybe, yes ) )
  @ "yes   and no    => " & ternary_image( ternary_and( yes, no ) )
  @ "yes   and maybe => " & ternary_image( ternary_and( yes, maybe ) )
  @ "yes   and yes   => " & ternary_image( ternary_and( yes, yes ) );
  new_line;

  ? "Ternary Or:"
  @ "no    or no    => " & ternary_image( ternary_or( no, no ) )
  @ "no    or maybe => " & ternary_image( ternary_or( no, maybe ) )
  @ "no    or yes   => " & ternary_image( ternary_or( no, yes ) )
  @ "maybe or no    => " & ternary_image( ternary_or( maybe, no ) )
  @ "maybe or maybe => " & ternary_image( ternary_or( maybe, maybe ) )
  @ "maybe or yes   => " & ternary_image( ternary_or( maybe, yes ) )
  @ "yes   or no    => " & ternary_image( ternary_or( yes, no ) )
  @ "yes   or maybe => " & ternary_image( ternary_or( yes, maybe ) )
  @ "yes   or yes   => " & ternary_image( ternary_or( yes, yes ) );
  new_line;
end ternary_logic;
Output:
$ spar ternary_logic.sp
Ternary Not:
not no    => Yes
not maybe => Maybe
not yes   => No

Ternary And:
no    and no    => No
no    and maybe => No
no    and yes   => No
maybe and no    => No
maybe and maybe => Maybe
maybe and yes   => Maybe
yes   and no    => No
yes   and maybe => Maybe
yes   and yes   => Yes

Ternary Or:
no    or no    => No
no    or maybe => Maybe
no    or yes   => Yes
maybe or no    => Maybe
maybe or maybe => Maybe
maybe or yes   => Yes
yes   or no    => Yes
yes   or maybe => Yes
yes   or yes   => Yes

Tcl

The simplest way of doing this is by constructing the operations as truth tables. The code below uses an abbreviated form of truth table.

package require Tcl 8.5
namespace eval ternary {
    # Code generator
    proc maketable {name count values} {
	set sep ""
	for {set i 0; set c 97} {$i<$count} {incr i;incr c} {
	    set v [format "%c" $c]
	    lappend args $v; append key $sep "$" $v
	    set sep ","
	}
	foreach row [split $values \n] {
	    if {[llength $row]>1} {
		lassign $row from to
		lappend table $from [list return $to]
	    }
	}
	proc $name $args \
	    [list ckargs $args]\;[concat [list switch -glob --] $key [list $table]]
	namespace export $name
    }
    # Helper command to check argument syntax
    proc ckargs argList {
	foreach var $argList {
	    upvar 1 $var v
	    switch -exact -- $v {
		true - maybe - false {
		    continue
		}
		default {
		    return -level 2 -code error "bad ternary value \"$v\""
		}
	    }
	}
    }

    # The "truth" tables; “*” means “anything”
    maketable not 1 {
	true false
	maybe maybe
	false true
    }
    maketable and 2 {
	true,true true
	false,* false
	*,false false
	* maybe
    }
    maketable or 2 {
	true,* true
	*,true true
	false,false false
	* maybe
    }
    maketable implies 2 {
	false,* true
	*,true true
	true,false false
	* maybe
    }
    maketable equiv 2 {
	*,maybe maybe
	maybe,* maybe
	true,true true
	false,false true
	* false
    }
}

Demonstrating:

namespace import ternary::*
puts "x /\\ y == x \\/ y"
puts " x     | y     || result"
puts "-------+-------++--------"
foreach x {true maybe false} {
    foreach y {true maybe false} {
	set z [equiv [and $x $y] [or $x $y]]
	puts [format " %-5s | %-5s || %-5s" $x $y $z]
    }
}
Output:
x /\ y == x \/ y
 x     | y     || result
-------+-------++--------
 true  | true  || true 
 true  | maybe || maybe
 true  | false || false
 maybe | true  || maybe
 maybe | maybe || maybe
 maybe | false || maybe
 false | true  || false
 false | maybe || maybe
 false | false || true 


True BASIC

Translation of: BASIC256
FUNCTION and3(a, b)
    IF a < b then LET and3 = a else LET and3 = b
END FUNCTION

FUNCTION eq3(a, b)
    IF a =  tDontknow or b = tDontKnow then
       LET eq3 = tdontknow
    ELSEIF a = b then
       LET eq3 = ttrue
    ELSE
       LET eq3 = tfalse
    END IF
END FUNCTION

FUNCTION longname3$(i)
    SELECT CASE i
    CASE 1
         LET longname3$ = "Don't know"
    CASE 2
         LET longname3$ = "True"
    CASE else
         LET longname3$ = "False"
    END SELECT
END FUNCTION

FUNCTION not3(b)
    LET not3 = 2-b
END FUNCTION

FUNCTION or3(a, b)
    IF a > b then LET or3 = a else LET or3 = b
END FUNCTION

FUNCTION shortname3$(i)
    LET shortname3$ = ("F?T")[i+1:i+1+1-1]
END FUNCTION

FUNCTION xor3(a, b)
    LET xor3 = not3(eq3(a,b))
END FUNCTION

LET tfalse = 0
LET tdontknow = 1
LET ttrue = 2

PRINT "Nombres cortos y largos para valores lógicos ternarios:"
FOR i = tfalse to ttrue
    PRINT shortname3$(i); "  "; longname3$(i)
NEXT i
PRINT
PRINT "Funciones de parámetro único"
PRINT "x"; " "; "=x"; "  "; "not(x)"
FOR i = tfalse to ttrue
    PRINT shortname3$(i); "  "; shortname3$(i); "    "; shortname3$(not3(i))
NEXT i
PRINT
PRINT "Funciones de doble parámetro"
PRINT "x"; " "; "y"; "  "; "x AND y"; "  "; "x OR y"; "  "; "x EQ y"; "  "; "x XOR y"
FOR a = tfalse to ttrue
    FOR b = tfalse to ttrue
        PRINT shortname3$(a); " "; shortname3$(b); "     ";
        PRINT shortname3$(and3(a,b)); "       "; shortname3$(or3(a,b)); "       ";
        PRINT shortname3$(eq3(a,b)); "        "; shortname3$(xor3(a,b))
    NEXT b
NEXT a
END
Output:
Igual que la entrada de Liberty BASIC.

V (Vlang)

Translation of: AutoHotkey

Note:

1) V (Vlang) doesn't have a true "ternary" operator, but unlike in other languages, its "if-else if-else" can be written on a single line.

2) This "trick" can make code more understandable, where a traditional ternary style could be visually confusing.

3) Something like this, "a=1?b:a=0?1:a+b>1?1:0.5" is instead "if a == 1 {b} else if a == 0 {1} else if a + b > 1 {1} else {0.5}"

Below is an example of how this would look, which can be compared to traditional ternary usage:

import math

fn main() {
    aa := [1.0,0.5,0]
    bb := [1.0,0.5,0]
    mut res :=''
    
    for a in aa {
        res += '\tTernary_Not\t' + a.str() + '\t=\t' + ternary_not(a) + '\n'
    }
    res += '-------------\n'
    
    for a in aa {
        for b in bb {
            res += a.str() + '\tTernary_And\t' + b.str() + '\t=\t' + ternary_and(a,b) + '\n'
        }
    }
    res += '-------------\n'
    
    for a in aa {
        for b in bb {
            res += a.str() + '\tTernary_or\t' + b.str() + '\t=\t' + ternary_or(a,b) + '\n'
        }
    }
    res += '-------------\n'
 
    for a in aa {
        for b in bb {
            res += a.str() + '\tTernary_then\t' + b.str() + '\t=\t' + ternary_if_then(a,b) + '\n'
        }
    }
    res += '-------------\n'
 
    for a in aa {
        for b in bb {
            res += a.str() + '\tTernary_equiv\t' + b.str() + '\t=\t' + ternary_equiv(a,b) + '\n'
        }
    }            
    res = res.replace('1.', 'true')
    res = res.replace('0.5', 'maybe')
    res = res.replace('0', 'false')
    println(res)
}

fn ternary_not(a f64) string {
    return math.abs(a-1).str()
}

fn ternary_and(a f64, b f64) string {
    return if a < b {a.str()} else {b.str()}
}

fn ternary_or(a f64, b f64) string {
    return if a > b {a.str()} else {b.str()}
}
 
fn ternary_if_then(a f64, b f64) string {
    return if a == 1 {b.str()} else if a == 0 {'1.'} else if a + b > 1 {'1.'} else {'0.5'}
}
 
fn ternary_equiv(a f64, b f64) string {
    return if a == b {'1.'} else if a == 1 {b.str()} else if b == 1 {a.str()} else {'0.5'}
}
Output:
	Ternary_Not	true	=	false
	Ternary_Not	maybe	=	maybe
	Ternary_Not	false	=	true
-------------
true	Ternary_And	true	=	true
true	Ternary_And	maybe	=	maybe
true	Ternary_And	false	=	false
maybe	Ternary_And	true	=	maybe
maybe	Ternary_And	maybe	=	maybe
maybe	Ternary_And	false	=	false
false	Ternary_And	true	=	false
false	Ternary_And	maybe	=	false
false	Ternary_And	false	=	false
-------------
true	Ternary_or	true	=	true
true	Ternary_or	maybe	=	true
true	Ternary_or	false	=	true
maybe	Ternary_or	true	=	true
maybe	Ternary_or	maybe	=	maybe
maybe	Ternary_or	false	=	maybe
false	Ternary_or	true	=	true
false	Ternary_or	maybe	=	maybe
false	Ternary_or	false	=	false
-------------
true	Ternary_then	true	=	true
true	Ternary_then	maybe	=	maybe
true	Ternary_then	false	=	false
maybe	Ternary_then	true	=	true
maybe	Ternary_then	maybe	=	maybe
maybe	Ternary_then	false	=	maybe
false	Ternary_then	true	=	true
false	Ternary_then	maybe	=	true
false	Ternary_then	false	=	true
-------------
true	Ternary_equiv	true	=	true
true	Ternary_equiv	maybe	=	maybe
true	Ternary_equiv	false	=	false
maybe	Ternary_equiv	true	=	maybe
maybe	Ternary_equiv	maybe	=	true
maybe	Ternary_equiv	false	=	maybe
false	Ternary_equiv	true	=	false
false	Ternary_equiv	maybe	=	maybe
false	Ternary_equiv	false	=	true

Wren

var False = -1
var Maybe =  0
var True  =  1
var Chrs  = ["F", "M", "T"]

class Trit {
    construct new(v) {
        if (v != False && v != Maybe && v != True) Fiber.abort("Invalid argument.")
        _v = v
    }

    v { _v }

    ! { Trit.new(-_v) }

    &(other)  { (_v < other.v) ? this : other }

    |(other)  { (_v > other.v) ? this : other }

    >>(other) { (-_v > other.v) ? !this : other }

    ==(other) { Trit.new(_v * other.v) }

    toString { Chrs[_v + 1] }
}

var trits = [Trit.new(True), Trit.new(Maybe), Trit.new(False)]

System.print("not")
System.print("-------")
for (t in trits) System.print(" %(t) | %(!t)")

System.print("\nand | T  M  F")
System.print("-------------")
for (t in trits) {
    System.write(" %(t)  | ")
    for (u in trits) System.write("%(t & u)  ")
    System.print()
}

System.print("\nor  | T  M  F")
System.print("-------------")
for (t in trits) {
    System.write(" %(t)  | ")
    for (u in trits) System.write("%(t | u)  ")
    System.print()
}

System.print("\nimp | T  M  F")
System.print("-------------")
for (t in trits) {
    System.write(" %(t)  | ")
    for (u in trits) System.write("%(t >> u)  ")
    System.print()
}

System.print("\neqv | T  M  F")
System.print("-------------")
for (t in trits) {
    System.write(" %(t)  | ")
    for (u in trits) System.write("%(t == u)  ")
    System.print()
}
Output:
not
-------
 T | F
 M | M
 F | T

and | T  M  F
-------------
 T  | T  M  F  
 M  | M  M  F  
 F  | F  F  F  

or  | T  M  F
-------------
 T  | T  T  T  
 M  | T  M  M  
 F  | T  M  F  

imp | T  M  F
-------------
 T  | T  M  F  
 M  | T  M  M  
 F  | T  T  T  

eqv | T  M  F
-------------
 T  | T  M  F  
 M  | M  M  M  
 F  | F  M  T  

Yabasic

Translation of: BASIC256
tFalse = 0
tDontKnow = 1
tTrue = 2

sub not3(b)
	return 2-b
end sub

sub and3(a,b)
	return min(a,b)
end sub

sub or3(a,b)
	return max(a,b)
end sub

sub eq3(a,b)
    if a = tDontKnow or b = tDontKnow then
       return tDontKnow
    elsif a = b then
       return tTrue
    else
       return tFalse
    end if
end sub

sub xor3(a,b)
    return not3(eq3(a,b))
end sub

sub shortName3$(i)
    return mid$("F?T", i+1, 1)
end sub

sub longName3$(i)
    switch i
        case 1
            return "Don't know"
        case 2
            return "True"
        default
            return "False"
    end switch
end sub

print "Nombres cortos y largos para valores logicos ternarios:"
for i = tFalse to tTrue
    print shortName3$(i), "  ", longName3$(i)
next i
print

print "Funciones de parametro unico"
print "x", " ", "=x", "  ", "not(x)"
for i = tFalse to tTrue
    print shortName3$(i), "  ", shortName3$(i), "    ", shortName3$(not3(i))
next i
print

print "Funciones de doble parametro"
print "x"," ","y","  ","x AND y","  ","x OR y","  ","x EQ y","  ","x XOR y"
for a = tFalse to tTrue
    for b = tFalse to tTrue
        print shortName3$(a), " ", shortName3$(b), "     ";
        print shortName3$(and3(a,b)), "       ", shortName3$(or3(a,b)), "       ";
        print shortName3$(eq3(a,b)), "        ", shortName3$(xor3(a,b))
    next b
next a
end
Output:
Igual que la entrada de Liberty BASIC.