
Dafny – A programming language with a program verifier - btat1
https://github.com/Microsoft/dafny/blob/master/README.md
======
Animats
That's very nice. It's surprisingly similar to the Pascal-F verifier I worked
on in 1983. I recently found the sources for that, and since they were
approved for release, I put them on Github.[1] (It's not runnable yet; passes
1 and 2 now work, but pass 4, which needs to be ported to a more modern LISP,
doesn't.) The approach is amazingly similar - automatic range and bounds
verification, entry and exit assertions, pure functions can be used in
assertions, additional lemmas can be proven.

Here are some Pascal-F examples that passed the verifier.[2] "bubble.pf" is a
bubble sort. "circle.pf" is a circular buffer. Everybody who does verification
work does those. Note how close these are to the similar Dafny examples. The
terminology is different but equivalent; Dafny uses "requires" and "ensures"
where Pascal-F used "entry" and "exit". Dafny used "old(f)" to talk about the
old value (at function entry) of a variable, while Pascal-F uses "f.old". Both
systems have lemmas, proved recursively with some extra effort by the user.

A more useful Pascal-F example is "engine1.pf", which is a simple engine
control program for an auto engine. This has concurrency and real time
constraints. Proof goals include proving that the spark is fired exactly once
per cylinder time, and that if the engine stops rotating, the fuel pump is cut
off.

Past this level, the next problem is scaleup. What do you want to prove about
a large program? These simple verifiers don't address that. However, this sort
of thing is really good at preventing buffer overflows and other things which
break the program in a low-level way.

I'd like to see Rust have Dafny's level of proof capability. This would help
deal with "unsafe" code where the programmer claims Rust's invariants are
preserved. Most of those situations just require proving that some assertion
holds both before and after some data manipulation, even though there are
moments when it doesn't hold. Verification technology is quite capable of
that, and has been for a long time.

(Having 3000x as much compute power as we had in 1982 helps a lot.)

[1] [https://github.com/John-Nagle/pasv](https://github.com/John-Nagle/pasv)
[2] [https://github.com/John-
Nagle/pasv/blob/master/src/Examples](https://github.com/John-
Nagle/pasv/blob/master/src/Examples)

~~~
btat1
Great job! I remember porting Fortran 77 programs on a Debian. The harder was
to find then to understand the old compiler documentation :)

~~~
Animats
Old Berkeley Pascal to Free Pascal wasn't too bad. Other than clashes over new
reserved words and conversion to modern I/O, that went well.

The LISP part, though, is not going well. Porting clever 1970s Stanford AI Lab
macros written on the original SAIL machine to modern Common LISP is hard.
Anybody with a knowledge of MACLISP want to help? The code is all on Github.
If you're into this, try to convert the macros "defsmac" and "defmac" in this
file [1] to clisp. Submit a pull request.

At least now both languages can do POSIX I/O. We had some implementation-
specific C glue code in the original just to talk to pipes. That can go away.

Looking to the future, Dafny level verification could easily deal with two of
the big unsafe areas in Rust - partially initialized arrays (used for
collections that can grow), and backpointers. Those have clear invariants,
easy proofs, and the proofs can be done by an automatic prover. You don't need
a labor-intensive interactive system like Coq.

The Dafny approach has the nice property that it's all right there in the
program source code. There's no separate verification data to be kept in sync.
We did that in Pascal-F, too, but many later systems have the user working in
a completely different mathematical notation for verification purposes. This
is a huge turnoff for programmers.

[1] [https://github.com/John-
Nagle/pasv/blob/master/src/CPC4/defm...](https://github.com/John-
Nagle/pasv/blob/master/src/CPC4/defmac.l)

~~~
pdw
I can't read MACLISP, but these two Common Lisp macros match the behavior
that's described in the big comment:

    
    
        (defmacro defsmac (name params expr)
          `(defmacro ,name ,params
             (sublis (mapcar 'cons ',params (list ,@params)) ',expr)))
        
        (defmacro defmac (name params expr)
          `(defmacro ,name ,params
             (list '(lambda ,params ,expr) ,@params)))

~~~
Animats
Thanks very much. It turns out the first one can be called with multiple args
in the "expr" slot, so it has to be

    
    
        (defmacro defsmac (name params &rest expr)
          `(defmacro ,name ,params
             `,(cons 'progn (sublis (mapcar 'cons ',params 
                    (list ,@params)) ', expr))))
    

but your code got me un-stuck.

------
vmarsy
This is a Microsoft Research project started in 2008 [1]. It was previously
hosted on codeplex [2]

[1] [https://www.microsoft.com/en-us/research/project/dafny-a-
lan...](https://www.microsoft.com/en-us/research/project/dafny-a-language-and-
program-verifier-for-functional-correctness/)

[2]
[http://dafny.codeplex.com/wikipage/history](http://dafny.codeplex.com/wikipage/history)

------
moomin
For those of you not familiar with Haskell, it's worth pointing out that types
and proofs are equivalent (with caveats). What this means in practice for
Dafny is that anything you can do in it can equally be expressed in a language
like Idris (with footnotes). Equally, it means that the trickiness encountered
in heavily typed functional programming languages is carried over directly to
working with program verifiers. At that point, it's more a case of what's
easier to grok and work with.

I mention this, because I see a lot of people who dismiss something like Idris
out of hand saying they want correctness checking, not realizing they're
basically equivalent.

~~~
amelius
But wouldn't it be much more cumbersome to check correctness using a type-
system?

~~~
tomp
Yes.

The main advantage of program verifiers (e.g. Dafny) is that there's no (or
very little) type/term division. A runtime function `+` is the same as the
proof/mathematical function `+`, runtime numbers are mathmatical numbers, etc.
(it gets a bit more complicated with equality AFAIK). As a result, in Haskell
you have to reimplement the whole (Peano) integer arithmetics again in the
type system to prove e.g. array bounds checks, whereas Dafny automatically
maps between proofs and code.

I think that Haskell is slowly doing away with this (they're unifing the term
and proof language). I think that Idris completely unifies the two. The only
difference that remains then is that Dafny uses an automated theorem prover
(Z3), whereas in Haskell/Idris you have to do proofs by yourself.

~~~
moomin
Idris has pluggable peoof strategies, so whilst it's not doing it all for you,
in theory you could plug Z3 into it.

------
seibelj
I had to use this for an algorithms class while getting my CS master's. Very,
very tough to use for anything beyond simple experiments. It has confusing
syntax and incomplete documentation. But when you finally get your program to
execute, you can be sure it's perfect! (Supposedly)

~~~
harpocrates
What were the things that were confusing? I tried it out and found it much
more intuitive than Coq (especially in the proofs). The keywords made sense,
and so did the error messages. That said, I come from a more math oriented
background, and I've played typed around with this sort of thing for a while
now. So yeah, I'm honestly interested in hearing your perspective!

~~~
seibelj
From what I remember, the assignment was to write a function that implemented
quick sort (or something else moderately complex) and getting it to run and
satisfy the proof checker was exhausting.

Edit: Found a forum post I made during it asking for help. Oh, the frustrating
memories...
[https://dafny.codeplex.com/discussions/546995](https://dafny.codeplex.com/discussions/546995)

~~~
harpocrates
I see. Googled the for Dafny quick sort and it does have some pretty long
invariants.

[1]: [http://rise4fun.com/Dafny/ks](http://rise4fun.com/Dafny/ks)

~~~
archgoon
Does Dafny not provide any macros for their invariants declarations ensures
clauses? The QuickSort Example has a lot of the same invariants again and
again, and most of them boil down to "I swear that the integer values are
valid indices into the array".

~~~
houli
Yes, you can define predicates that take parameters and use them as the
invariants

------
didibus
What are the competitors to this language? Can someone provide a list. I'd
like to start learning a provable language, not sure if Dafny is the one I
should start with though.

~~~
SilasX
Per this reddit comment [1], the languages supporting some kind of compile-
time constraint-checking/proving include Why3, Dafny, Fstar, Coq, Agda, and
Idris.

Also, some languages have been extended to support it: LiquidHaskell, Frama-C,
and Spark/Ada. I also recall some Java extension but can't find it atm.

[1]
[https://www.reddit.com/r/ProgrammingLanguages/comments/4ref8...](https://www.reddit.com/r/ProgrammingLanguages/comments/4ref8v/can_pls_have_richer_more_general_compiletime/d50gwpb/?utm_content=permalink&utm_medium=front&utm_source=reddit&utm_name=ProgrammingLanguages)

~~~
igravious
Per the Wikipedia page for Whiley[1] the languages akin to Dafny are mentioned
when explaining what Whiley is: “The primary purpose of such a tool is to
improve software quality by ensuring a program meets a formal specification.
In other words, to help identify and eliminate software bug in the programs
being developed. Whiley follows many attempts to develop such tools, including
notable efforts such as SPARK/Ada, ESC/Java, Spec#, Dafny, Why3 and Frama-C.”

I'm thinking there must be a reason they do not mention F*, Coq, Agda, and
Idris, perhaps because they are proof assistants[2] rather than automatic
provers of programs with formal specifications. See also Isabelle and others.
Regarding Idris (which you mention but is not on that page) and Epigram (which
is noteworthy and again not on that page), neither are production ready and
the latter is unmaintained.

[1]
[https://en.wikipedia.org/wiki/Whiley_(programming_language)#...](https://en.wikipedia.org/wiki/Whiley_\(programming_language\)#Overview)

[2]
[https://en.wikipedia.org/wiki/Proof_assistant](https://en.wikipedia.org/wiki/Proof_assistant)

~~~
tom_mellior
> I'm thinking there must be a reason they do not mention F* , Coq, Agda, and
> Idris

The ones they do mention are all _imperative_ languages with added deductive
verification in a Hoare calculus style (think loop invariants). The ones you
mention are _functional_ languages with a different style of proof (think
inductive proofs).

Of course there are overlaps between the two styles (notably in Why3), but
there are pronounced differences between them.

~~~
igravious
Ok. But that's not what I was getting at.

------
pron
For those interested, Java has a mature specification language called JML,
with tools similar to those provided by Dafny[1] and many more. In addition to
SMT solvers, you can use interactive proofs for obligations that are not
automatically discharged[2][3], or use the specifications to generate concolic
tests[4].

[1]: [http://www.openjml.org/](http://www.openjml.org/)

[2]: [http://www.key-project.org/index.php](http://www.key-
project.org/index.php)

[3]: [http://krakatoa.lri.fr/](http://krakatoa.lri.fr/)

[4]: [https://github.com/Rafael-Baltazar/jmlcute](https://github.com/Rafael-
Baltazar/jmlcute)

------
yairharel
I love the thoughtful logo

------
vbit
Would be nice to have a screenshot without the red squiggly underlines.

~~~
dualogy
Unless..... "As you type in your program, the verifier constantly looks over
your shoulders and flags any errors." .... it's a _feature being highlighted_
=)

------
gravypod
"Try Dafny" is not complete. It doesn't have a link to request a special
purpose keyboard to be sent to you.

~~~
saghm
Assuming it's anything like the Emacs mode for Coq, they're probably just
using an editor/editor plugin that replaces the operators/keywords with the
mathematical symbols.

------
awinter-py
so cool! every language should have an invariant keyword.

not sure how one uses it in practice -- most of the operators don't exist on
my keyboard.

~~~
Animats
I had the same argument with the Stanford Pascal Verifier people around 1980.
They were using the SAIL PDP-10, with keyboards with extra characters and
extra shift keys TOP and META. Assertions were written with mathematical
notation but code was written in ASCII. We stuck to ASCII and Pascal operators
for Pascal-F.

Today, everybody has Unicode output, and all the math symbols are potentially
typeable, but it drives programmers nuts if they have to enter them. If you're
going to do a verification system, you have to design it for programmers, not
mathematicians, or it won't be used. This is a serious problem with formal
methods. The formalism is a means to an end, not an end in itself. The end
goal is bug-free code.

Dafny for Rust has potential. One big problem is where to put the assertions
inside terse functional notation. This is word wrap in Rust:

    
    
        s.lines()
            .map(|bline| UnicodeSegmentation::graphemes(bline, true)   
                .collect::<Vec<&str>>())
            .map(|line| wordwrapline(&line, maxline, maxword))
            .collect::<Vec<String>>()        
        .join("\n")
    

OK, where would we put assertions? Maybe intersperse

    
    
        .assert(|arg| assertionexpression)
    

which is just a passthrough that returns its value but allows making
assertions about what's going through it.

