Interview Checklist for Software Engineers

Technical

  1. do they have opinions about software engineering?
  2. are those opinions sensible?
  3. do those opinions conflict too strongly with our existing team?
  4. do they really want to make good software?
  5. do they really want to make the software we are making?
  6. are they interested in users?
  7. do they bring skills we don’t already have?
  8. do they have the self discipline to work effectively within a team?
  9. do they have the self discipline to make good quality software?
  10. is their personality such that will they work effectively within our team?
  11. are they creative and clever and self-motivated to apply it?

Non-Technical

  1. do they have good negotiation skills?
  2. can they explain themselves clearly?
  3. can they learn something quickly? (do they listen to others?)
  4. do they know how to learn in the medium and long term?
  5. are they honest and insightful about the limits of their own knowledge and abilities?
    1. do they know what they can do already?
    2. do they know what they can’t do yet?
    3. are they willing to tell me what they can’t do yet?
    4. are they trying to blag?
    5. are they self-motivated?
    6. do they have something they want to achieve from working?
      (money, status, skills, friends, fun, whatever)?
  6. do they like to share what skills they have with others?

Start-ups only

  1. do they understand what a start-up is (in business terms)?
  2. do they understand what a start-up needs?
  3. do they understand the risks of a start-up?

Distributivity of Sequence Map over Function Composition in Dafny

Distributivity of Sequence Map over Function Composition in Dafny

lemma MapDistributivity(xs:seq<int>, f:int->int, g:int->int)
   requires forall x :: x in xs ==> f.requires(x);
   requires forall x :: x in xs ==> g.requires(f(x));
   ensures forall x :: x in MapSeq(xs,f) ==> g.requires(x);
   ensures MapSeq(MapSeq(xs,f), g) == MapSeq(xs, Compose(f,g));
{
   if xs != []
   {
     MapDistributivity(xs[1..], f, g);
   }
}
 
function Compose(f:int->int, g:int->int) : int->int
{
  x
    reads f.reads(x)
    reads if f.requires(x) then g.reads(f(x)) else {} 
    requires f.requires(x) 
    requires g.requires(f(x)) 
      -> g(f(x))
}
 
function MapSeq(xs:seq<int>, f:int->int) : seq<int>
   reads MapSeqReads(xs, f);
   requires forall x :: x in xs ==> f.requires(x);
   ensures |xs| == |MapSeq(xs,f)|;
   ensures forall x :: x in xs ==> f(x) in MapSeq(xs,f); 
{
  if xs == [] then []
  else [f(xs[0])] + MapSeq(xs[1..], f) 
}
 
function MapSeqReads(xs:seq<int>, f:int->int) : set<object>
   reads if |xs| > 0 then f.reads(xs[0]) + MapSeqReads(xs[1..], f) else {};
   decreases xs;
   ensures forall x :: x in xs ==> f.reads(x) <= MapSeqReads(xs,f); 
{
  if xs == [] then {}
  else f.reads(xs[0]) + MapSeqReads(xs[1..],f)
}

Dafny: Permutations, sequences and multisets

Dafny has support for multisets, which are really useful for reasoning about programs that permute, or otherwise deal with permutations, of sequences or arrays.

predicate Subpermutation(xs:seq, ys:seq)
   ensures Subpermutation(xs,ys) ==> forall x :: x in xs ==> x in ys;
{
  assert forall x :: x in xs ==> x in multiset(xs);
  multiset(xs) <= multiset(ys)
}
 
// THEOREM
lemma SubpermutationIsSmaller(xs:seq, ys:seq)
  requires Subpermutation(xs,ys);
  ensures |xs| <= |ys|;
{
  assert |multiset(xs)| == |xs|;
  assert |multiset(ys)| == |ys|;
 
  var xs',ys' := xs,ys;
 
  var XS',YS' := multiset(xs),multiset(ys);
  var XS'',YS'' := multiset{},multiset{};
  while(|XS'|>0)
     invariant Subpermutation(xs',ys');
     invariant XS' == multiset(xs');
     invariant YS' == multiset(ys');
     invariant XS' + XS'' == multiset(xs);
     invariant YS' + YS'' == multiset(ys);
     invariant XS'' == YS'';
     invariant XS' <= YS';
  {   
    assert RemoveFromSequenceReducesMultiSet(xs,multiset(xs),multiset(xs[1..]));
    var x := xs'[0];
    xs' := Remove(x,xs');
 
    ys' := Remove(x,ys');
 
    XS' := XS'[x := XS'[x] - 1];
    XS'' := XS''[x := XS''[x] + 1];
    YS' := YS'[x := YS'[x] - 1];
    YS'' := YS''[x := YS''[x] + 1];    
  }
}
 
// SUPPORTING DEFINITIONS
 
// following is a function lemma
predicate RemoveFromSequenceReducesMultiSet(xs:seq<T>, XS:multiset<T>, XS':multiset<T>)
   requires xs != [];
   requires XS' == multiset(xs[1..]);
   ensures XS' == multiset(xs)[xs[0] := multiset(xs)[xs[0]] - 1];
   ensures RemoveFromSequenceReducesMultiSet(xs,XS,XS');
{
  assert [xs[0]]+xs[1..] == xs;
  assert multiset([xs[0]]+xs[1..]) == multiset(xs);  
  true
}
 
function Remove(x:T, xs:seq<T>) :seq<T>
   requires x in xs;
   ensures multiset(Remove(x,xs)) == multiset(xs)[x := multiset(xs)[x] - 1];
   ensures |Remove(x,xs)|+1 == |xs|;
{
     assert RemoveFromSequenceReducesMultiSet(xs,multiset(xs),multiset(xs[1..]));
     if xs[0]==x 
     then xs[1..] 
     else [xs[0]] + Remove(x,xs[1..])
}

Dafny Triggers

It is possible to write tiggers for particular quantifiers in Dafny programs.

predicate P(x:int) { true } 
 
lemma ThereIsMoreThanOneInteger(x:int)
   ensures exists z:int :: z!=x;
{ 
  // can't prove
}
 
lemma NoReallyThereIsMoreThanOneInteger(x:int)
   ensures exists z:int {:trigger P(z)} :: z!=x;
{ 
  assert P(x+1);
  // can prove
}

Dafny Function Lemmas

Sometimes it is hard to use a normal Dafny method lemma where you need it. For example, if you want to use a lemma inside a function or in a contract. There is a simple solution.

Given a method lemma:

static lemma SomethingValid(s:S)
   requires P(s);
   ensures Q(s);

We can produce a function lemma:

static lemma SomethingValidFn(s:S)
   requires P(s);
   ensures Q(s);
   ensures SomethingValidFn(s);
{
   SomethingValid(s); true
}

Dafny: Proving forall x :: P(x) ==> Q(x)

The general method in Dafny for proving something of the form forall x :: P(x) ==> Q(x) is:

lemma X()
    ensures forall x :: P(x) ==> Q(x);
{
    forall x | P(x)
        ensures Q(x);
    {
       // prove Q(x)
    }
}

Using {:verify false} in Dafny

Due to the nature of the underlying SMT used by Dafny for proof checking, Dafny sometimes takes a very long time to verify things. I find it useful to ask Dafny to work on a single lemma at a time. This can be achieved using the {:verify false} annotation. I set every function and method in my proof to {:verify false}, then change only the one I am working on back to {:verify true}. Occasionally I use find and replace to change all the {:verify false} annotations to {:verify true} and then run the whole proof through the Dafny command line.

Opaque functions in Dafny (performance/proveablity,readbility)

I am using Opaque functions in Dafny quite heavily, and for three main reasons:

Opaque functions can improve performance

I have a very large predicate which judges partial isomorphism between two program states. This predicate appears in the requires, ensures and bodies of a large number of lemmas and functions in my proof. The number of facts that are introduce by Dafny automatically revealing these function definitions seems to substantially slow down the proof step, sometimes to the point where it no longer goes through in an amount of time I am willing to wait.

For example take a predicate like this

prediate I(a:T,b:T)
{
   P(a,b) && Q(a,b) && R(a,b) && S(a,b)
}
predicate P(a:T,b:T) { ... }
predicate Q(a:T,b:T) { ... }
...
static lemma IThenSomethingThatReliesOnQ(a:T,b:T)
  requires I(a,b);
  ensures SomethingThatReliesOnQ(a,b);
{
   // Q automatically revealed, but so is P,R and S
}

Instead we can write

prediate I(a:T,b:T)
{
   P(a,b) && Q(a,b) && R(a,b) && S(a,b)
}
predicate {:opaque true} P(a:T,b:T) { ... }
predicate {:opaque true} Q(a:T,b:T) { ... }
...
static lemma IThenSomethingThatReliesOnQ(a:T,b:T)
  requires I(a,b);
  ensures SomethingThatReliesOnQ(a,b);
{
   reveal_Q(); // reveals Q only, not P,R and S
}

Opaque functions can improve readability

I initially really liked the automatic function definition revealing feature, but as my proof got larger I liked it less. Particularly in the presence of big predicates (like my isomorphism predicate), I found it can get quite hard to understand which parts of the proof rely on which facts. This made it harder for me to work out what I need to establish in other parts of the proof (in this case, that the isomorphism has certain properties and that those can be used to show it is preserved by some particular program execution steps).

The example above shows how using opaque and reveal makes the proof more self documenting, allows us to automatically check that documentation and provides (I think) more insight into the proof.

Opaque functions help you find places that need re-verifying

If you are using {:verify false} to restrict re-verification only to the elements you are working on, then having functions set to opaque helps you find all the places that depend on their definition. So, if you change the definition then you can easily find all the places that will need to be re-verified with the new definition.

Naming quantifiers in Dafny

It seems to help Dafny with instantiation of quantifiers during proofs if you give the quantified expression a name by encapsulating it in a predicate. If you don’t you can end up in a situation where you prove some quantified property, but are then unable to subsequently assert that quantified property, I think due to Dafny not understanding that the quantification is the same. Naming the property (or more likely, only writing it in one place rather than two places) fixes this.

For example rather than writing:

   ...
   ensures forall c :: P(a, c) == P(b, c);
   ...
   assert forall c :: P(a, c) == P(b, c);

Write this:

   predicate Q(a:S,b:S)
   { forall c :: P(a, c) == P(b, c) }
   ...
   ensures Q(a, b);
   ...
   assert Q(a, b);

Building Dafny Visual Studio Extension

You will need the Visual Studio 2012 SDK installed you can tell this because Visual Studio tells you the project is incompatible and refuses to open it. But if you use a text editor to look at the associated .csproj file, that file has the GUID “82b43b9b-a64c-4715-b499-d71e9ca2bd60” in the “project types” section. And this GUID means “needs the visual studio SDK”.

You probably need to build boogie first. You may find that you need to enable nuget restore (right click the Boogie Solution in the Solution Explorer and click Enable NuGet Package Restore) for the boogie solution, in order to get visual studio to download the correct version of nunit.

Then you need to open the solution Dafny.sln in the source directory, and build it with visual studio. Then build the visual studio extension DafnyExtension.sln. Then in “Extensions and Updates” you can uninstall the DafnyLanguageMode if you already have it. Then you can install Binaries\DafnyLanguageService.vsix to get the VS extension.

Also, on the Dafny VS extension color codes. I think perhaps: yellow means “line changed since last save”; orange means “line changed since the last verify run”; pink means “line currently being verified”; and green means line verified.

Build Boogie

  1. Get the latest code (its now on github)
  2. Open Source/Boogie.sln in visual studio
  3. Build -> Build

Build Dafny

  1. Get the latest code (hg pull the hg update)
  2. Open Source/Dafny.sln in visual studio
  3. Build -> Build
  4. Open Source/DafnyExtension.sln in visual studio
  5. Build -> Build

Documentation

  1. Dafny type system documentation