[racket] rho-contracts.js: Racket-style contracts for plain JavaScript

From: Guillaume Marceau (gmarceau at gmail.com)
Date: Mon May 20 18:08:28 EDT 2013

Hi all,


In case this might interest some folks here. I now work for a start up in
New York. We're doing physics-based analysis of green buildings. I just
received permission to release our JavaScript implementation of Racket's
contracts under an open source license, MPL to be precise.

https://github.com/sefaira/rho-contracts.js


After some fiddling, I was able to create a syntax that's readable enough
(I hope). Here is the `derive` function wrapped in a contract:

var c = require('contract')

// define the contract that's used below (since it's used twice):
c.numberToNumber = c.fun( { x: c.number } ).returns(c.number)

// derive: returns a function that is the numerically-computed derivative
//         of the given function.
var derive =
  /* This is the specification: */
  c.fun( { fn:     c.numberToNumber },
         { deltaX: c.number         } )
   .returns(c.numberToNumber)
   .wrap(
         /* And the implementation goes here: */
         function(fn, deltaX) {
           return function(x) {
            return (fn(x+deltaX/2) - fn(x-deltaX/2))/deltaX
           }
         })


Once the contract is in place, you get the kind of error message you always
wanted to have in JavaScript, but never could, such as the basic "wrong
number of arguments.", and you also get contract error with blame (though
only function-level blame, not between-module blame):

  // Error: forgetting an argument:
  > derive(quadratic)
  ContractError: Wrong number of arguments, expected 2 but got 1

  // Error: calling with the arguments flipped:
  > derive(1.0, quadratic)
  ContractError: Expected fun, but got 1
  For the `fn` argument of the call.

  // Error: calling with the wrong kind of function:
  > var d = derive(function(x) { return "**" + x + "**" }, 1.0)

  // The contract-checking shell is now installed around `fn` inside of `d`;
  // throws an error when called:
  > d(100)
  ContractError: `fn()` broke its contract
  Expected number, but got '**100.5**'
  for the return value of the call.



There is also support for placing contract on whole module at a time.
Notably, the contract library itself is wrapped with contracts, as it
should:

https://github.com/sefaira/rho-contracts.js/blob/master/contract.face.js


This has been in production at sefaira.com for about a year now. We're
really happy to have a piece of Racket-lore be our first open source
release. Hopefully there will be more.


Best,

Guillaume
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.racket-lang.org/users/archive/attachments/20130520/debe7fe1/attachment-0001.html>

Posted on the users mailing list.