clojure和scala_学习Clojure:依赖类型和基于合同的编程

clojure和scala

在描述如何处理动态类型时,我们使用了规范库。 该库不是类型的真正替代-检查是在运行时而不是编译时执行的。 另一方面,它可以超越单纯的类型,包括模拟依赖类型和按合同编程。

这是在学习Clojure的焦点series.Other职位包括5 职位:

  1. 解码Clojure代码,让您不知所措
  2. 学习Clojure:应对动态打字
  3. 学习Clojure:arrow和doto宏
  4. 学习Clojure:动态调度
  5. 学习Clojure:依赖类型和基于契约的编程 (本文)
  6. 学习Clojure:与Java流进行比较
  7. 关于学习Clojure的反馈:与Java流进行比较
  8. 学习Clojure:换能器

依赖类型

让我们从定义什么是依赖类型开始:

在计算机科学和逻辑中,从属类型是其定义取决于值的类型。 “整数对”是一种类型。 由于对值的依赖性,“第二对大于第一对的整数对”是从属类型。

—维基百科
https://zh.wikipedia.org/wiki/Dependent_type

让我们尝试使用spec对以上定义中提到的类型进行建模。

显然,“整数对”可以建模为大小为2的集合,其中包含按设置顺序包含int

我们将使用增量方法:

  1. 第一个模型int的集合
  2. 然后在大小上加一个限制
  3. 最后检查价值订购

让我们开始对int的集合进行建模。 在第一篇文章中看到的coll-of函数非常适合指定这样的集合。

请记住,其参数是应用于集合中每个元素的谓词
(s/def ::ints (s/coll-of int?))
(s/valid? ::ints [2 "2"])         ; false
(s/valid? ::ints ["4" "4"])       ; false
(s/valid? ::ints [3 5])           ; true
(s/valid? ::ints [2])             ; true
(s/valid? ::ints [2 2 2])         ; true

现在,让我们将元素数量限制为2,以形成一对。 相关的代码非常简单,因为函数coll-of允许:count键指定所需的元素数量:

(s/def ::pair-of-ints (s/coll-of int? :count 2))   (1)
(s/valid? ::pair-of-ints [2 2])                  ; true
(s/valid? ::pair-of-ints [4 4])                  ; true
(s/valid? ::pair-of-ints [3 5])                  ; true
(s/valid? ::pair-of-ints [2])                    ; false
(s/valid? ::pair-of-ints [2 2 2])                ; false
  1. 任何两个int

现在该添加最终要求了:第二个值必须大于第一个值。 显然这是一个从属类型,因为它意味着检查值。

  • 这需要为此创建谓词检查:
    (defn ordered?
      [pair]
      (> (last pair) (first pair)))
  • 然后,我们需要组成这个自定义谓词和一个开箱即用的coll-of 通过适当命名的布尔宏andor可以使这种组合成为可能。
    (s/def ::dependent (s/and (s/coll-of int? :count 2)  (1)
                              ordered?))
    (s/valid? ::dependent [2 2])                       ; false
    (s/valid? ::dependent [3 4])                       ; true
    (s/valid? ::dependent [2 1])                       ; false
    (s/valid? ::dependent [1 2 3])                     ; false

就是这样,我们定义了(运行时)依赖类型。 所有构建块均可用于构建更复杂的类型。

但是,这还不是全部。 类型-不管是否依赖,本身都不有用。

如果我们可以将它们与功能结合起来怎么办?

基于合同的编程

仅像上一节中那样设置依赖类型不是很有用:它需要在每个变量上显式调用(s/valid) ,这很笨拙。 不过,Clojure还提供了另一个选项。

我已经写过关于在Java(Java和Kotlin) 上使用JVM进行合约式编程( 又名基于合约式编程)的文章。 Clojure允许在执行函数主体之前和之后调用不确定数量的函数。 这可以通过定义一个特定于所需函数的映射来实现:pre:post分别接受要在函数之前和之后执行的谓词数组。

将这些前/后执行与规范结合起来,即可实现基于合同的编程。

让我们创建一个add函数,将上述两对相加。 该函数应类似于:

(defn add
  [p1, p2]
  "Add two pairs"
  (let [s1 (+ (first p1) (first p2))
        s2 (+ (last p1) (last p2))]
    [s1 s2]))
  1. p1p2都必须通过::dependent进行验证。 转换为:
    {:pre  [(s/valid? ::dependent p1),
            (s/valid? ::dependent p2)]}
  2. 返回的值也应使用相同的谓词进行验证,因为如果两者的第一项都低于第二项,则添加项不应对此进行更改。 要引用该值,请使用%
    {:post (s/valid? ::dependent %)}

最后的功能是:

(defn add
  [p1, p2]
  {:pre  [(s/valid? ::dependent p1),
          (s/valid? ::dependent p2)]
   :post (s/valid? ::dependent %)}
  (let [s1 (+ (first p1) (first p2))
        s2 (+ (last p1) (last p2))]
    [s1 s2]))

它可以这样称呼:

(add [1 2] [3 4])    ; [4 6]
(add [2] [3 4])      ; AssertionError
(add ["1" 2] [3 4])  ; AssertionError
(add [1 2] 4)        ; AssertionError

结论

在这篇文章中,我们描述了如何超越单纯的类型,以及对具有依赖类型的值施加约束。 结合按合同进行编程,这对使代码更可靠确实有帮助。

翻译自: https://blog.frankel.ch/learning-clojure/4/

clojure和scala

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值