我的新职位要求我熟悉Clojure语言 。 打算记录我在一系列帖子中学到的内容,以作为我的个人参考笔记。 作为副作用,我希望它对希望走同样道路的其他人也将是有益的。 考虑到我的大部分经验都来自OOP ,因此已经有大量出色的教程可供使用:因此,每篇文章都将专注于特定主题,该主题特定于Clojure。
这是在学习Clojure的焦点series.Other职位包括第2 个职位:
- 解码Clojure代码,让您不知所措
- 学习Clojure:应对动态类型化 (本文)
- 学习Clojure:arrow和doto宏
- 学习Clojure:动态调度
- 学习Clojure:依赖类型和基于合同的编程
- 学习Clojure:与Java流进行比较
- 关于学习Clojure的反馈:与Java流进行比较
- 学习Clojure:换能器
作为Clojure的新手,我的一个大问题是缺乏类型。 这不是Clojure特有的。 我错过了JavaScript,Groovy,Python等中的类型。尽管我重视动态语言在编写脚本时的易用性,但我的主要任务仍然是开发常规应用程序。 考虑到这一点,我更喜欢让编译器捕获与类型相关的错误:这意味着着眼于实际的业务功能,而不是编写测试来捕获这些错误。
尽管Clojure不提供语言语法中的类型,但其设计允许通过构造来构建类似的功能。 更好的是,有一个适当的命名规范spec来处理这个问题 。
| 自Clojure 1.9起,即可立即使用spec。 早期版本需要显式添加对类路径的依赖。 |
基本
要开始使用spec,只需在名称空间中要求
1 2 | ( ns ch.frankel.blog.clojure.spec ( :require [ clojure.spec.alpha :as sparc ])) |
下一步是使用
| # | 名称 | 描述 |
|---|---|---|
|
1个 |
|
符号名称 |
|
2 |
|
谓语 |
| 可能会有更多有效的参数值,但这足以开始。 |
对于简单的值,这非常简单:
1 2 3 | ( spec/def ::nil nil? ) <i class="conum" data-value="1"></i> <b class="raw_b_node">(1)</b> ( spec/def ::bool boolean? ) <i class="conum" data-value="2"></i> <b class="raw_b_node">(2)</b> ( spec/def ::string string? ) <i class="conum" data-value="3"></i> <b class="raw_b_node">(3)</b> |
| 1个 | 将 |
| 2 | 将 |
| 3 | 将 |
— Clojure文档 |
该技术不限于简单类型。 也可以将值限制为枚举:
1 | ( spec/def ::direction # { ::NORTH ::EAST ::SOUTH ::WEST }) |
规格检查
定义规范后,有两种不同的使用方法。
-
valid? 函数返回一个boolean ,具体取决于值是否符合spec 例如 :
资源 符合吗? 退货 1( spec/valid? ::nil nil )true 1( spec/valid? ::string "f" )true 1( spec/valid? ::nil "f" )false 1( spec/valid? ::string nil )false -
conform? 函数返回:- 值是否符合
spec - 或
clojure.spec.alpha/invalid 如果clojure.spec.alpha/invalid
资源 符合吗? 退货 1( spec/conform? ::nil nil )nil 1( spec/conform? ::string "f" )"f" 1( spec/conform? ::nil "f" )clojure.spec.alpha/invalid 1( spec/conform? ::string nil )clojure.spec.alpha/invalid - 值是否符合
自定义规格功能
上面的代码使用了现成的函数, 例如
| 功能 | 检查参数是否为... |
|---|---|
|
|
关键字(显然是...) |
|
|
一个符号(显然也是如此) |
|
|
符号或关键字 |
|
|
一个 |
|
|
一个 |
虽然涵盖了一些用例,但并未涵盖许多特定用例。 在这种情况下,可以使用任何接受参数并返回
这是一个检查参数是否为
1 2 3 4 5 6 7 8 9 | ( defn local-date? "Check if the parameter is a java.time.LocalDate instance" [ x ] ( instance? LocalDate x )) ( spec/def ::date local-date? ) ( spec/valid? ::date ( LocalDate/of 2009 1 1 )) <i class="conum" data-value="1"></i> <b class="raw_b_node">(1)</b> ( spec/valid? ::date "f" ) <i class="conum" data-value="2"></i> <b class="raw_b_node">(2)</b> |
| 1个 | 评估为 |
| 2 | 评估为 |
规范数据结构
现在我们知道如何指定简单的值,是时候指定更复杂的值了。 在Clojure中,对“实体”建模的一种常用方法是使用数据映射。
我最喜欢的示例是具有以下属性的
- 名字
- 姓
- 生日
可以使用
1 2 3 4 5 6 | ( spec/def ::first-name string? ) ( spec/def ::last-name string? ) ( spec/def ::birthdate local-date? ) ( spec/def ::person ( spec/keys :req [ ::first-name ::last-name ] <i class="conum" data-value="1"></i> <b class="raw_b_node">(1)</b> :opt [ ::birthdate ])) <i class="conum" data-value="2"></i> <b class="raw_b_node">(2)</b> |
| 1个 | 必要值 |
| 2 | 可选值 |
以下是一些示例以及一些相关的有效性检查:
| 资源 | 退货 | 基本原理 | ||
|---|---|---|---|---|
|
|
|||
|
|
|
||
|
|
|
||
|
|
不需要 |
||
|
|
|
||
|
|
其他条目也可以 |
规格集合
下一步是使用规范来验证集合中元素的类型,就像Java中的泛型一样, 例如
-
coll-of 为“标准”的Clojure集合,vector 或list 等。 -
map-of 地图
例如,指定
1 | ( spec/def ::dates ( spec/coll-of ::date )) |
同样,对于
1 | ( spec/def ::map-dates ( spec/map-of keyword? ::date )) |
当然,它也适用于数据结构:
1 | ( spec/def ::map-persons ( spec/map-of keyword? ::person )) |
结论
尽管Clojure是一种动态类型的语言,但可以通过使用
更进一步:
- 规格指南
关注@nicolas_frankel
翻译自: https://blog.frankel.ch/learning-clojure/1/