Updates to contracts.coffee
An update to contracts.coffee (0.2.0) is now up on npm. I've fixed up a few bugs since the intial release and it's now based on the latest CoffeeScript (1.2.0) but the big update is a rework of how modules are handled.
Before this update we were handling module blame tracking by forcing the
programmer to call .use()
before a contracted value could be used. This
did the work of figuring out who the server and client module.
This was an awkward way to enforce a module pattern since not only do you have to write more code but it also means you have to know which values in a module have a contract. This breaks abstraction which is bad.
So with this latest update we're completely dropping .use()
. How we keep tack
of the modules without it depends on whether you are using node.js or a browser.
If you are using node.js then the wiring is handled automatically by wrapping
the require
function and exports
object. If you put a contract on an export
value the correct server and client module names are recorded.
# file Math.coffee exports.square :: (Num) -> Pos exports.square = (x) -> x * x
#####
# file Main.coffee {square} = require './Math'
# vioation blaming Main.coffee square "a string"
You can also put a contract on a value that isn't on the exports
object (for use inside
the module), but the module name might get confusing since it's just the file name with
"(value)" or "(caller)" appened. I will look at adding something like Racket's
nested boundaries
to clean this up at some point.
If you're using the browser, things are a bit more complicated. Since the browser environment
doesn't currently have a standard way to do modules, we're stuck doing the wiring by hand.
Details are in the documentation but the basic idea is that
the library now provides Contracts.exports
and Contracts.use
which are used to construct
contract aware modules.
Contracts.exports
creates and names an exports object:
# Math.coffee
# create and name the exports object exports = Contracts.exports "Math"
exports.square :: (Num) -> Pos exports.square = (x) -> x * x
# put the exports object on the global object # for other modules to see and use window.Math = exports
And Contracts.use
pulls in a module and names the user of the module.
# Main.coffee {square} = Contracts.use window.Math, "Main"
square 4 # 16 square "a string" # Contract Violation...blaming Main
I'll look at ways to automate this more in future updates.
- Previous: Performance of contracts.coffee
- Next: Occupying Language Design at POPL