2013-07-03 36 views
1

我認爲按合同設計是一種有用的技術,並且希望將其應用於我的coffeescript代碼。不帶語言擴展的coffeescript的合同設計支持

contracts.coffee,它看起來真的不錯(比如Haskell):

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

缺點是,它是一種語言的擴展。我很猶豫,因爲我害怕用工具支持交易麻煩。 (我是不是太保守了?)

雖然它看起來不錯,但我更喜歡目前的圖書館解決方案。對於紅寶石,我最近發現contracts.ruby,共享相同的優雅,但具有的優點是,它只是普通的紅寶石:

require 'contracts' 
include Contracts 

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

是否有類似的東西了CoffeeScript的?

我讀了約jsContracts,但還沒有測試過。似乎是一個有用的庫,但它缺乏Ruby DSL或contracts.coffee語言擴展的優雅。

問題:

  • 是否有語法不錯的設計,通過合同庫的CoffeeScript(或JavaScript)的無縫集成常見的工具鏈?

  • 我對合同咖啡的擔憂是否合理? (如果不是,它似乎是完美的結合。)

回答

1

它極易內的CoffeeScript定義自己的DSL。如果你想創建一個類型檢查框架,例如,你可以創建一個類這樣

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

@def應該創建一個方法名爲「foo」,並通過調用給定的功能檢查,根據自己的位置它的參數「params」數組。

讓我們寫一些測試第一

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

這時我們就需要一些輔助方法,將做實際的參數檢查

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 

也許他們應該做更有用的東西(比如拋出一個異常)。就我們的例子而言,它們應該足夠了

現在我們的類A調用一個尚未定義的類方法。我們將創建一個基類,這和它

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 
    ... 

顯然,在這幾個疣

  • 參數數組和參數數組可以是不同lenght的繼承我們的A類(沒有檢查該還)
  • 的輔助函數應該拋出一個異常
  • 輔助功能應該是組合像isNotNull(ISNUM)
  • 你規避定義一個方法,這樣的「正常」的方式你造成的JavaScript代碼將是難以閱讀和調試 - 也許不是

以下是完整的運行代碼在一個去

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 

其大致1/3的相應Javascript代碼的長度,當然它的通信意圖要好得多(imo)