1

I consider design-by-contract an useful technique and want to apply it to my coffeescript code.

There is contracts.coffee, which looks really nice (like Haskell):

id :: (Num) -> Num
id = (x) -> x

Downside is that it is a language extension. I'm hesitating because I'm afraid to trade in trouble with tool support. (Am I too conservative?)

Though it really looks great, I would prefer a library solution at the moment. For Ruby, I recently found contracts.ruby, which shares the same elegance but has the advantage that it is just plain Ruby:

require 'contracts'
include Contracts

Contract Num => Num
def id(x) ; x ; end

Is there something similiar for coffeescript?

I read about jsContracts but haven't tested it. Seems to be a useful library, but it lacks the elegance of the Ruby DSL or the contracts.coffee language extension.

Questions:

  • Is there a syntactically nice design-by-contract library for coffeescript (or Javascript) that integrates seamlessly into the common toolchains?

  • Are my concerns about contracts.coffee justified? (If not, it seems to be the perfect fit.)

Philipp Claßen
  • 41,306
  • 31
  • 146
  • 239

2 Answers2

1

See this question: Is there a code contract library for JavaScript?

You can use https://npmjs.org/package/contracts-js, which is the sort of backend, if you will, to contracts.coffee. The downside is that it requires proxies, which are not supported very well in front-end JavaScript.

Seems like an interesting idea for a different kind of library, maybe one that extends functions with contracts...

Community
  • 1
  • 1
maxov
  • 341
  • 2
  • 4
1

Its extremely easy to define your own DSL within CoffeeScript. If you want to create a type checking framework you could for example create a class like this

class A
     @def foo:
          params: [isNum, isBool,isNotNull]
          body: (x, y, z) -> console.log "foo: #{x}, #{y}, #{z}"

@def should create a method named "foo" and check its parameters according to their position by calling the functions given in the "params" array.

Lets write some test first

a = new A()
a.foo 3, true, "foo"
a.foo "string", true, "foo"
a.foo 3, "string", "foo"
a.foo 3, false, null

Then we need some helper methods that will do the actual parameter checking

isNum = (p)-> console.log "p isnt a number its => #{p}" if typeof p != "number"
isBool = (p)-> console.log "p isnt a bool its => #{p}" if typeof p != "boolean"
isNotNull = (p)-> console.log "p is null" if p == null or p == undefined

Probably they should do something more useful (like throwing an exception). For our example they should be sufficient.

Now our class A calls a class-method which is not yet defined. We will create a base class for this and the inherit our class A from it

class ContractBase
    @def: (fndef)->
        #get the name of the "function definition" object
        #should be the only key
        name = Object.keys(fndef)[0]
        #get the real function body
        fn = fndef[name]["body"]
        #get the params
        params = fndef[name]["params"]

        # create a closure and assign it to the prototype
        @::[name] = ->
            #check the parameters first
            for value, index in arguments
                #get the check at the index of the argument
                check = params[index]
                #and run it if available
                check(value) if check
            #call the real function body
            fn arguments...

#and finally change A to extend from ContractBase
class A extends ContractBase
    ...

Obviously there are a few warts in it

  • The arguments array and the parameter array can be of different lenght (there is no check for that yet)
  • The helper functions should throw an exception
  • the helper functions should be combinable like isNotNull(isNum)
  • you are circumventing the "normal" way of defining a method so your resulting javascript code will be harder to read and to debug - maybe not

Here is the full running code in one go

class ContractBase
    @def: (fndef)->
        name = Object.keys(fndef)[0]
        fn = fndef[name]["body"]
        params = fndef[name]["params"]
        @::[name] = ->
            for value, index in arguments
                check = params[index]
                check(value) if check
            fn arguments...

isNum = (p)-> console.log "p isnt a number its => #{p}" if typeof p != "number"
isBool = (p)-> console.log "p isnt a bool its => #{p}" if typeof p != "boolean"
isNotNull = (p)-> console.log "p is null" if p == null or p == undefined

class A extends ContractBase
    @def foo:
        params: [isNum, isBool,isNotNull]
        body: (x, y, z) -> console.log "foo: #{x}, #{y}, #{z}"

a = new A()
a.foo 3, true, "foo"
a.foo "string", true, "foo"
a.foo 3, "string", "foo"
a.foo 3, false, null

Its roughly 1/3 of the length of the corresponding Javascript code and certainly much more readable as it communicates intent much better (imo)

robkuz
  • 9,488
  • 5
  • 29
  • 50