Justin Pombrio

What we preceive as reality is a construct of the mind.

Invariants across languages

Invariants help us reason about our programs. Herein lies a list of invariants you might expect to hold, and a table showing whether or not they hold for four languages: Haskell, C, Python, and Javascript.



 #======================#
 | First, some notation |
 #======================#


  x, y, z - values
  A, B, C - expressions
  f, g, h - function names
  v, w    - variable names / identifiers

Here is the correspondence between our notation, and the syntax of the
four languages in question,
  
  Notation
    x == y
    var v = A
    func f(x, y) {A}
  
  Javascript
    x === y
    var v = A;
    var f = function(x, y) {A};
  
  Python
    x == y
    v = A
    def f(x, y):
      A
  
  C
    x == y
    <type> v = A;
    <type> f(x, y) {A}
  
  Haskell
    x == y
    v = A
    f x y = A
  
A[B/v] means A, with B substituted for (unbound occurrances of) v.

A === B means that A and B are equivalent (interchangable)
expressions.

 #================#
 | The invariants |
 #================#


    Arithmetic
     ~~~~~~~~
(For standard library +, *, -, /)

   Identity
A0.  x + 0 = 0 + x = x
A1.  x * 1 = 1 * x = x
   Commutativity
A2.  x + y = y + x
A3.  x * y = y * x
   Associativity
A4.  (x + y) + z = x + (y + z)
A5.  (x * y) * z = x * (y * z)
   Inverse
A6.  x - x = 0
A7.  x / x = 1.0
   Distributivity
A8.  (x + y) * z = (x * z) + (y * z)

A2 fails in python because "hi " + "there" != "there" + "hi ".


    Booleans
     ~~~~~~

B0.  if A then B else C  ===  if A == true then B else C


    Comparisons
     ~~~~~~~~~

C0.  x == x
C1.  If x < y then not y < x
C2.  x < y or y < x or x == y
C3.  x <= y iff not y < x
C4.  If x < y and y < z then x < z

The existence of NaN breaks #0-#3. Every language considered has NaN,
so #4 is the only property with a hope of surviving. It may hold in C,
Python, Haskell.

It does _not_ hold in Javascript:
"2" < 3 && 3 < "04" && "04" < "2"


    Variable Definitions
     ~~~~~~~~~~~~~~~~~~

D0.  "A variable may be replaced by its definition."

     var v = A; B   ===   B[A/v]

D0'. "In pure code, a variable may be replaced by its definition."

     var v = A; B   ===   B[A/v],    A, B pure


    Functions
     ~~~~~~~

F0.  "A function may be replaced by its definition."

     func f() {A}; B   ===   B[A/f()]

F0'. "In pure code, a function may be replaced by its definition."

     func f() {A}; B   ===   B[A/f()],   A, B pure

F1.  "Calling a function is the same as evaluating its arguments and
      then evaluating its body with the parameters replaced by the
      evaluated arguments."

     func f(x) {A}; f(B)   ===   var x = B; A[*x/x]


    Miscellany
     ~~~~~~~~

M0.  "Identifiers may be escaped without changing their meaning."
     id   ===   \u0069\u0064


Fun fact - in Ocaml,
  let update x = lift2 LocMap.add x   [[[type checked]]]
  let update   = lift2 LocMap.add     [[[didn't]]]



 #==============#
 | The results! |
 #==============#


H - Haskell
C - C
P - Python
J - Javascript

1 - Holds
0 - Does not hold

    H C P J
A0  1 1 1 0
A1  1 1 1 0
A2  1 1 0 0
A3  1 1 1 0
A4  0 0 0 0
A5  0 0 0 0
A6  0 0 0 0
A7  0 0 0 0
A8  0 0 0 0
B0  1 0 0 0
C0  0 0 0 0
C1  0 0 0 0
C2  0 0 0 0
C3  0 0 0 0
C4  1 1 1 0
D0  1 0 0 0
D0' 1 1 1 0
F0  1 0 0 0
F0' 1 0 0 0
F1  1 1 0 0
M0  1 1 1 0

This table is _conservative_. Where there is a 0, I know of a
counterexample. But the 1s are merely my best guesses; perhaps some of
them should be 0s. I'd be interested to hear of counterexamples.

FYI, this is why I prefere Haskell to Javascript.