我认为按合同设计是一种有用的技术,并希望将其应用于我的coffeescript代码。
有contracts.coffee,看起来非常好(如Haskell):
id :: (Num) -> Num
id = (x) -> x
下行是它是一种语言扩展。我在犹豫,因为我害怕用工具支持来解决问题。 (我太保守了吗?)
虽然它看起来很棒,但我现在更喜欢图书馆解决方案。对于Ruby,我最近发现了contracts.ruby,它具有相同的优雅,但它的优势在于它只是简单的Ruby:
require 'contracts'
include Contracts
Contract Num => Num
def id(x) ; x ; end
coffeescript有类似的东西吗?
我读过关于jsContracts的内容,但尚未对其进行测试。似乎是一个有用的库,但它缺乏Ruby DSL或contract.coffee语言扩展的优雅。
问题:
是否有一个语法上很好的coffeescript(或Javascript)合同设计库,它可以无缝集成到常用的工具链中?
我对contract.coffee的担忧是否合理? (如果没有,它似乎是最合适的。)
答案 0 :(得分:1)
请参阅此问题:Is there a code contract library for JavaScript?
你可以使用https://npmjs.org/package/contracts-js,这是一种后端,如果你愿意的话,可以使用contract.coffee。缺点是它需要代理,前端JavaScript不能很好地支持代理。
对于不同类型的库来说,似乎是一个有趣的想法,也许是一个用契约扩展函数的库...
答案 1 :(得分: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调用一个尚未定义的类方法。我们将为此创建一个基类,并从中继承我们的类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
...
显然它中有一些瑕疵
这是一次性完整运行的代码
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
它大约是相应Javascript代码长度的1/3,当然更可读,因为它更好地传达意图(imo)