sbvPlugin-0.11: Formally prove properties of Haskell programs using SBV/SMT

Copyright(c) Levent Erkok
LicenseBSD3
Maintainererkokl@gmail.com
Stabilityexperimental
Safe HaskellNone
LanguageHaskell2010

Data.SBV.Plugin.Examples.MicroController

Contents

Description

A transcription of Anthony Cowley's MicroController example, using the SBV plugin. For the original, see: http://acowley.github.io/NYHUG/FunctionalRoboticist.pdf

Synopsis

Parameters

safetyDistance :: Int Source #

The range detector must output if the range is larger than this amount.

maxTimeSince :: Int Source #

The range detector must have sent an output before this many cycles have past.

The specification

checkSpec :: (Int -> Bool -> Int -> Int) -> Int -> Bool -> Int -> Bool Source #

Given a last-signal-time calculator, named calculate, check that it satisfies the following three requirements: We must've just sent a signal if:

  • minRate: The last-time we sent is strictly less than the maxTimeSince amount
  • minRange: We must've just sent a signal if the range is beyond safetyDistance
  • manualOverride: We must've just sent a signal if the manual-override is specified

A bad implementation

computeLastBad :: Int -> Bool -> Int -> Int Source #

A "bad" implementation, see if you can spot the problem with it, before looking at the failed theorem below!

checkBad :: Int -> Bool -> Int -> Bool Source #

Using SBV, prove that the computeLastBad is indeed a bad implementation. Here's the output we get from the plugin:

  [SBV] MicroController.hs:85:1-8 Proving "checkBad", using Z3.
  [Z3] Falsifiable. Counter-example:
    range     =   200 :: Int64
    manual    = False :: Bool
    timeSince =     9 :: Int64

We're being told that if the range is 200, and manual override is off, and time-since last is 9, then our "calculator" returns 10. But that violates the minRate requirement, since we never want to go maxTimeSince cycles without sending a signal!

A correct implementation

computeLastGood :: Int -> Bool -> Int -> Int Source #

A "good" implementation, properly handling the off-by-one error of the original.

checkGood :: Proved (Int -> Bool -> Int -> Bool) Source #

We now verify that the good variant is indeed good. We have:

  [SBV] MicroController.hs:108:1-9 Proving "checkGood", using Z3.
  [Z3] Q.E.D.

Exercise for the reader

 

It is easy to see that an implementation that always returns 0 (i.e., one that always sends a signal) will also pass our specification!

  • First, use the plugin to prove that such an implementation is indeed accepted.
  • Then, modify the spec so that we require the timeSince parameter to actually get incremented under the correct conditions.
  • Show that your new spec outlaws the always 0 producing implementation.