© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
B. SitnikovskiIntroducing Software Verification with Dafny Languagehttps://doi.org/10.1007/978-1-4842-7978-6_9

9. Implementing a Formal System

Boro Sitnikovski1  
(1)
Skopje, North Macedonia
 

So far, we have been mostly using a set of formal systems to prove software correctness. In this final chapter, we show how to both create and use a formal system in order to be able to prove facts. For that, we will provide a minimal implementation1 of propositional logic, as described in Chapter 2. For a more advanced implementation of a formal system, see [7].

The syntax of the formal system expressed in BNF (Backus-Naur form) is
1  prop ::= P | Q | R | unop prop | prop brelop prop
2  unop ::= "!"
3  brelop ::= "&&" | "||" | "->"
The code in Dafny, which follows the same syntax:
1  datatype Prop =
2    P | Q | R
3    | Not (Prop)
4    | And (Prop, Prop)
5    | Or (Prop, Prop)
6    | Imp (Prop, Prop)
We now have a way to represent some logical formulas, for example, ?? as And(P, Q). In Dafny, we get construction of well-formed formulas for free due to algebraic data structures. In some untyped programming languages, such as Python, we could use hashmaps to simulate the types.
1  # Python
2  def And(x, y):
3    return {'x': x, 'y': y, 'type': 'and'}
4
5  def Imp(x, y):
6    return {'x': x, 'y': y, 'type': 'imp'}
Further, in our implementation, we also need a way to differentiate between well-formed formulas and theorems since not all well-formed formulas are theorems. For that, we provide the Proof data type and a way to extract a proof:
1  //datatype Prop = ...
2  datatype Proof<T> = Proof(T)
3  function method FromProof(x : Proof<Prop>) : Prop
4  {
5    match x {
6      case Proof(a) => a
7    }
8  }
Note that Proof(And(P, Q)) (⊢ ??) is different from And(P, Q) (??). Here’s how we can construct and destruct (extract) proofs in Python:
1  # Python
2  def Proof(x):
3    return {'v': x, 'type': 'proof'}
4  def FromProof(x):
5    if not isinstance(x, dict) or not 'v' in x: return None
6    return x['v']

Note in the Python code how we have to be extra careful with the conditional checks, whereas in Dafny, it is much simpler due to pattern matching.

The Proof constructor mustn’t be used directly; proofs should only be constructed given the rules that we provide next.
 1  function method RuleJoin(x : Proof<Prop>, y : Proof<Prop>) :
        Proof<Prop>
 2  {
 3    Proof(And(FromProof(x), FromProof(y)))
 4  }
 5  function method RuleSepL(x : Proof<Prop>) : Proof<Prop>
 6  {
 7    match FromProof(x) {
 8      case And(a, b) => Proof(a)
 9      case x => Proof(x)
10    }
11  }
The preceding implementation corresponds to the following rules:
$$ frac{Akern0.36em B}{Awedge B}left(mathtt{RuleJoin}
ight)kern0.36em frac{Awedge B}{A}left(mathtt{RuleSepL}
ight) $$
These rules come from GEB [1], and we list all of them here for completeness:
  • Joining Rule (And-introduction): If ? and ? are theorems, then ? ∧ ? is a theorem.

  • Sep Rule (And-elimination): If ? ∧ ? is a theorem, then both ? and ? are theorems.

  • Fantasy Rule (Implication-introduction): If ? were a theorem, ? would be a theorem (? → ?).

  • Contrapositive Rule: ? → ? and ¬? → ¬? are interchangeable.

  • De Morgan’s Rule: ¬? ∧ ¬? and ¬(? ∨ ?) are interchangeable.

  • Double-Tilde Rule (Double negation): The string ¬¬ can be deleted from any theorem. It can also be inserted into any theorem.

  • Rule of Detachment (Implication-elimination): If ? and ? → ? are both theorems, then ? is a theorem.

  • Switcheroo Rule: ? ∨ ? and ¬? → ? are interchangeable.

The most powerful rule is the Fantasy Rule, implemented as follows:
1  function method RuleFantasy(x : Prop, y : (Proof<Prop> ->
      Proof<Prop>)) : Proof<Prop>
2  {
3    Proof(Imp(x, FromProof(y(Proof(x)))))
4  }

RuleFantasy accepts a non-proven term x : Prop, whereas other rules accept proven terms; the hypothesis needn’t be necessarily true, it only states that “If this hypothesis were a theorem, then that would be a theorem.” The second argument is a function y : (Proof<Prop> -> Proof<Prop>) that accepts a Proof and returns a Proof, basically another rule that will be used to transform the hypothesis x. As a result, it produces the theorem ? → y(?).

Here’s the same implementation of the previous rules in Python:
 1  # Python
 2  def RuleJoin(x, y):
 3    if not isinstance(x, dict) or not isinstance(y, dict) or
         not 'type' in x or not 'type' in y or x['type'] !=
         'proof' or y['type'] != 'proof': return None
 4    return Proof(And(FromProof(x), FromProof(y)))
 5  def RuleSepL(x):
 6    if not isinstance(x, dict) or not 'type' in x or x['type']
         != 'proof': return None
 7    wff = FromProof(x)
 8    if wff['type'] == 'and': return Proof(wff['x'])
 9    return Proof(wff)
10  def RuleFantasy(x, y):
11    if not isinstance(x, dict) or not 'type' in x or not
         callable(y): return None
12    return Proof(Imp(x, FromProof(y(Proof(x)))))
For most rules, we can write intro (RuleJoin) and elim (RuleSepL) functions. Here’s another example of a rule that performs interchanging of formulas:
1  function method RuleDeMorgan(x : Proof<Prop>) : Proof<Prop>
2  {
3    match FromProof(x) {
4      case And(Not(a), Not(b)) => Proof(Not(Or(a, b)))
5      case Not(Or(a, b)) => Proof(And(Not(a), Not(b)))
6      case x => Proof(x)
7    }
8  }
Since we defined the rules in Dafny as function method, we can build proofs iteratively by printing the results. For example, we can prove that ⊢ ???? as follows:
 1  method Main()
 2  {
 3    var p_and_q := And(P, Q);
 4    var join := (x) => RuleJoin(RuleSepL(x), RuleSepL(x));
 5    var x := RuleFantasy(p_and_q, join);
 6    print(x);
 7    assert x == Proof(Imp(p_and_q, And(P, P)));
 8  }
 9  // Or simply
10  method Example()
11    ensures RuleFantasy(And(P, Q), (x) => RuleJoin(RuleSepL(x),
          RuleSepL(x))) == Proof(Imp(And(P, Q), And(P, P)))
12  {}

Note how we embedded a formal system (propositional logic) within a formal system (Dafny/Python), and we were able to reason about it through symbolic manipulation; in Dafny, this corresponds to pattern matching and data types, whereas in Python, it corresponds to conditionals and hashmaps.

Exercise 1 Implement the remainder of the rules in Dafny or your favorite programming language.

Exercise 2 Using the rules you implemented in the previous exercise, prove the following fact in the system: ¬¬(¬??) → ??.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.217.68.197