Hi,
We implemented a tool to allow design-by-contract in Go by automatically 
generating pre and postcondition checks in code: 
https://github.com/Parquery/gocontracts. 

However, the most interesting part of the design-by-contract, namely 
invariants, is still missing and I'd like to hear your feedback on how to 
implement it. 

In an OO language, invariants should hold after the constructor and after 
invocation of each public method. They are particularly interesting way to 
formalize the properties on your more complex contained data structures. 
For example, if you internally use a sorted slice to perform binary search 
on it for interval queries, an invariant would be that the slice is always 
sorted. Another example is a directed acyclic graph with an invariant that 
there should be no cycles.

For a more detailed (and better) article on advantages of 
design-by-contract in general and invariants in particular, please see: 
https://cacm.acm.org/blogs/blog-cacm/227928-why-not-program-right/fulltext

Right now, gocontracts operates on individual files. To add the invariants 
to a type, I thought about passing the package folder (instead of 
individual files) to gocontracts, inspecting the type comments and 
iterating through all the public methods associated with the type. Since 
there are no constructors in Go, I'd establish the invariants in all the 
public methods as additional pre and postconditions.

I though that a documentation defining the invariants could look like this:

// SomeType represents something.
//
// SomeType establishes:
// * x >= 0
// * x < Width
// * x < somePrivateProperty
type SomeType struct {
   // ...
}

For some method:

// SomeFunc does something.
//
// SomeFunc requires:
// * x % 3 == 0
func (s *SomeType) SomeFunc(x int) {
   // ...
}


gocontracts would append the invariants of the type to the pre and 
postconditions and generate the checks as:

// SomeFunc does something.
//
// SomeFunc requires:
// * x % 3 == 0
func (s *SomeType) SomeFunc(x int) {
   // Pre-conditions
   switch {
   case !(s.x >= 0):
      // panic ...
   case !(x % 3 == 0):
      // panic ...
   // ...
   }

   // Post-conditions
   defer func() {
      switch {
      case !(s.x >= 0):
         // panic ...
      // ...
      }
   }()
   // ...
}


How do you like the idea? Is the syntax appealing? Would you mind if you 
could only disable/enable checks on per-package basis?

In case gocontracts would still keep on operating on individual files, 
would it be confusing if it parsed all the other go files in the file's 
directory to read the invariants and then apply it to only the given file 
(and not establishing invariants on the functions defined in other files)?

Thanks a lot for your feedback!
Marko

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to