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)