- Lightweight and fast Self-Documenting Design by Contract Programming, Design by Contract, zero cost at runtime.
- Security Hardened mode (based from Debian Hardened & Gentoo Hardened, checked with hardening-check).
- Change Immutable variables, into Immutable variables, Immutable programming.
- Produces no code at all when build for release, KISS
- Works on NimScript, JavaScript,
{.compiletime.}andstatic:.
import contra
func funcWithContract(mustBePositive: int): int =
preconditions mustBePositive > 0, mustBePositive > -1 ## Require (Preconditions)
postconditions result > 0, result < int32.high ## Ensure (Postconditions)
result = mustBePositive - 1 ## Mimic some logic, notice theres no "body" block
discard funcWithContract(2)
# discard funcWithContract(0) # Uncomment to see it fail as expected.import contra
hardenedBuild() # Security Hardened mode enabled, compile with: -d:hardened
echo "Hello World"import contra
type Person = object # Changing Immutable Variables,into Immutable Variables.
name: string
age: Natural
let
bob = Person(name: "Bob", age: 42) # Immutable Variable, original.
olderBob = bob.deepCopy: # Immutable Variable, but changed.
this.age = 45
this.name = this.name[0..^2]
echo bob # (name: "Bob", age: 42) Original Immutable
echo olderBob # (name: "Bo", age: 45) Changed ImmutableIts inspired by Scala:
val immutableButChanged = immutable.copy(attribute = 9)assert(conditionBool, errorString) + echo(Nim_Code) + printf(C_Code) Combined "3-in-1".
- Is different than
echothat you dont have to manually remove it from the code for Release builds. - Is different than
assertthat it prints to terminal whenconditionBoolistrue.
It only works when not defined(release) and not defined(danger) for Debugging purposes.
C Source code debug is similar to JS Source Maps, shows C code corresponding to the same Nim code.
The assertion is a vanilla assert, when fails produces an AssertionError.
Produces No code at all when build for Release, zero runtime performance cost. assercho is a Macro.
assercho when assert is true (Ok)
import contra
let foo = 42
let bar = 9
assercho(foo > bar, "Assercho for all the Brochachos!") # 42 > 9Nim foo > bar = true --> /home/juan/code/example.nim(3, 8)
C (bar_1kqJRPNteJWdjlPR09aibuA < foo_FvFJfeatIb8qi8DN7lzoAQ) = 1 --> /tmp/example.nim.c(9)assercho when assert is false (Error)
import contra
let foo = 42
let bar = 9
assercho(foo < bar, "Assercho for all the Brochachos!") # 42 < 9Nim foo < bar = false --> /home/juan/code/example.nim(3, 8)
C (bar_1kqJRPNteJWdjlPR09aibuA < foo_FvFJfeatIb8qi8DN7lzoAQ) = 0 --> /tmp/example.nim.c(9)
Error: unhandled exception: `foo < bar` Assercho for all the Brochachos! [AssertionError]Replaces approx the following code:
when not defined(danger) and not defined(release):
debugEcho "foo = ", foo
debugEcho "bar = ", bar
debugEcho foo > bar
assert foo > bar, "Error message"
# Custom Macro to get the Nim Source Code line info of origin (macros.NimNode.LineInfo)
# Custom Macro/Template to get the C Source Code line info of originecho()anddebugEcho()gets Rewritten to usefwrite()fromstdio.hdirectly. Logging is not touched.fwrite()is faster thanputs(),puts()is faster thanprintf().fwrite() > puts() > printf().- Optimizations are optional, only get enabled
when defined(release) and defined(danger).
echo "a", "b", "c"Gets optimized to:
fwrite("abc\012", ((unsigned int) 1), ((unsigned int) 4), stdout);- Float Division is slower than multiplication. 1 CPU Cycle for Multiplication, 100 CPU Cycles or more for Division.
- Float Division gets Rewritten to multiplication with the inverse.
- Example
x / 3.0-->x * static(1.0 / 3.0). - Optimizations are optional, only get enabled
when defined(release) and defined(danger).
var x, y = 2.0
echo x / 2.0Gets optimized to:
NF x_9b3J8iZeIHRoRKYxMY9a9bzzQ;
x_9b3J8iZeIHRoRKYxMY9a9bzzQ = 2.0000000000000000e+00;
( (NF)(x_9b3J8iZeIHRoRKYxMY9a9bzzQ) * (NF)(5.0000000000000000e-01) );Contract Preconditions:
preconditionstakes preconditions separated by commas, asserts on arguments or local variables.
Contract Postconditions:
postconditionstakes postconditions separated by commas, must assert onresult, can assert on local variables.
Contracts Preconditions and Postconditions:
postconditionsmust be AFTERpreconditions.postconditionsmust NOT be repeated.-d:contractsForce enable Contracts, can be used independently of-d:release.
Security Hardened Mode:
-d:hardenedForce enable Security Hardened mode, can be used independently of-d:release.-d:hardenedrequires-d:contracts.- Security Hardened mode only works for default target backend.
- Produces no code at all if
-d:hardenedis not defined. hardenedBuild()is 1 Template, takes no arguments, returns nothing.hardenedBuild()must be called on the root top of your main module.- Hardened build is ideal companion for a Contracts module, still optional anyway.
Changing Immutable Variables:
deepCopyLets you change Immutable Variables, into Immutable Variables, using Mutated copies. It mimic Scala'sval immutableButChanged = immutable.copy(attribute = 9). Immutable programming.
Assercho:
assert+echo=assercho. Assertive programming util.
nimble install contra
- Why not just use Contracts ?
$ cat example.nim
import contracts
from math import sqrt, floor
proc isqrt[T: SomeInteger](x: T): T {.contractual.} =
require:
x >= 0
ensure:
result * result <= x
(result+1) * (result+1) > x
body:
(T)(x.toBiggestFloat().sqrt().floor().toBiggestInt())
echo isqrt(18)
echo isqrt(-8)
$ nim js -r example.nim
Error: undeclared identifier: 'deepCopy'
$ nim e example.nim
Error: undeclared identifier: 'deepCopy'
$ cat example2compiletime.nim
import contracts
from math import sqrt, floor
proc isqrt[T: SomeInteger](x: T): T {.contractual, compiletime.} =
require:
x >= 0
ensure:
result * result <= x
(result+1) * (result+1) > x
body:
(T)(x.toBiggestFloat().sqrt().floor().toBiggestInt())
echo isqrt(18)
echo isqrt(-8)
$ nim c -r example2compiletime.nim
Error: request to generate code for .compileTime proc: isqrt
$ cloc ~/.nimble/pkgs/contracts-0.1.0/
Language files blank comment code
----------------------------------------------------------------
Nim 21 119 515 640
- Whats Contract Programming, Design by Contract?.
https://www.youtube.com/watch?v=DRVoh5XiAZo
https://en.wikipedia.org/wiki/Defensive_programming#Other_techniques
http://stackoverflow.com/questions/787643/benefits-of-assertive-programming
https://en.wikipedia.org/wiki/Hoare_logic#Hoare_triple
- What about No Side Effects?.
https://nim-lang.org/docs/manual.html#procedures-func
https://nim-lang.org/docs/manual.html#pragmas-nosideeffect-pragma
- What about Types?.
https://nim-lang.org/docs/manual_experimental.html#concepts
- How to use this at Compile Time?.
Add {.compiletime.} or static:.
- What about
assumeblocks?.
Assume blocks produce no code at all and are only meant for human reading only,
you can do that using discard or similar contruct on Nim. KISS.
- What about
bodyblocks?.
This library does NOT uses nor needs body blocks.
- What about
invariantblocks?.
You can pass Invariants on the postconditions block.
- What about
forallandforsomeblocks?.
Use sequtils.filterIt, sequtils.mapIt, sequtils.keepItIf, sequtils.allIt, sequtils.anyIt, etc.
- What about
ghostblock?.
Use when defined(release): or when defined(contracts):
- Whats the performance and speed cost of using Contra?.
Zero cost at runtime, since it produces no code at all when build for Release.
- I prefer the naming
requireandensure?.
from contra import preconditions as require
from contra import postconditions as ensure- I prefer the naming
preandpost?.
from contra import preconditions as pre
from contra import postconditions as post- If I add this to my project I am forced to use it everywhere?.
No.
The code will just work on blocks without Contract. You only need to add 2 lines to your existing code (1 for Preconditions, 1 for Postconditions). Is recommended to at least use it con "core" functionality.
- Whats Hardened mode ?.
https://en.wikipedia.org/wiki/Hardening_%28computing%29#Binary_hardening
- More Documentation?.
nim doc contra.nim
" TDD is Poor-Man's Contracts "


