clojure_学习Clojure:应对动态打字

clojure

我的新职位要求我熟悉Clojure语言 。 打算记录我在一系列帖子中学到的内容,以作为我的个人参考笔记。 作为副作用,我希望它对希望走同样道路的其他人也将是有益的。 考虑到我的大部分经验都来自OOP ,因此已经有大量出色的教程可供使用:因此,每篇文章都将专注于特定主题,该主题特定于Clojure。

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

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

作为Clojure的新手,我的一个大问题是缺乏类型。 这不是Clojure特有的。 我错过了JavaScript,Groovy,Python等类型的信息。尽管我重视动态语言在编写脚本时的易用性,但我的主要任务仍然是开发常规应用程序。 考虑到这一点,我更愿意让编译器捕获与类型相关的错误:这意味着着眼于实际的业务功能,而不是编写测试来捕获这些错误。

尽管Clojure不提供语言语法中的类型,但其设计允许通过构造来构建类似的功能。 更好的是,有一个适当的命名规范spec来处理这个问题

自Clojure 1.9起,即可立即使用spec。 早期版本需要显式添加对类路径的依赖。

基本

要开始使用spec,只需在名称空间中要求 clojure.spec.alpha包即可:

(ns ch.frankel.blog.clojure.spec
  (:require [clojure.spec.alpha :as sparc]))

下一步是使用def函数定义值的预期类型。 它接受两个参数:

名称 描述

1

k

Symbol name

2

spec-form

Predicate

可能会有更多有效的参数值,但这足以开始。

对于简单的值,这非常简单:

(spec/def ::nil nil?)         (1)
(spec/def ::bool boolean?)    (2)
(spec/def ::string string?)   (3)
  1. ::nil定义为nil
  2. ::bool定义为boolean
  3. ::string定义为任何string
关键字是自我评估的符号标识符。 它们提供了非常快速的相等性测试。 像符号一样,它们具有名称和可选的名称空间,它们都是字符串。 前导“:”不是名称空间或名称的一部分。 — Clojure文档https://clojure.org/reference/data_structures#Keywords ::语法是限定关键字的快捷方式,该限定关键字使用当前名称空间进行完全限定。 例如,上面的:: bool解析为:ch.frankel.blog.clojure.spec / bool。

该技术不限于简单类型。 也可以将值限制为枚举:

(spec/def ::direction #{::NORTH ::EAST ::SOUTH ::WEST})

规格检查

定义规范后,有两种不同的使用方法。

  1. valid? 函数返回一个boolean ,取决于值是否符合spec 例如
    资源 符合吗? 退货
    (spec/valid? ::nil nil)

    true

    (spec/valid? ::string "f")

    true

    (spec/valid? ::nil "f")

    false

    (spec/valid? ::string nil)

    false

  2. conform? 函数返回:
    • 值是否符合spec
    • clojure.spec.alpha/invalid如果clojure.spec.alpha/invalid
    资源 符合吗? 退货
    (spec/conform? ::nil nil)

    nil

    (spec/conform? ::string "f")

    "f"

    (spec/conform? ::nil "f")

    clojure.spec.alpha/invalid

    (spec/conform? ::string nil)

    clojure.spec.alpha/invalid

定制规格功能

上面的代码使用了现成的函数, 例如 nil? string? 。 有很多类似的功能。 这是一个示例,摘自clojure.core

功能 检查参数是否为...

keyword?

a keyword (obviously…​)

symbol?

a symbol (obviously as well)

ident?

a symbol or a keyword

uuid?

a java.util.UUID instance

uri?

a java.net.URI instance

虽然涵盖了一些用例,但并未涵盖许多特定用例。 在这种情况下,可以使用任何接受参数并返回boolean函数。

这是一个检查参数是否为LocalDate以及如何使用的函数:

(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))  (1)
(spec/valid? ::date "f")                      (2)
  1. 评估为true
  2. 评估为false

规范数据结构

现在我们知道如何指定简单的值,是时候指定更复杂的值了。 在Clojure中,对“实体”建模的一种常见方法是使用数据映射。

我最喜欢的示例是具有以下属性的Person实体:

  • 名字
  • 生日

可以使用keys功能进行指定。 参数允许指定哪些键是必需的,哪些是可选的:

(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]   (1)
                              :opt [::birthdate]))              (2)
  1. 必要值
  2. 可选值

以下是一些示例以及一些相关的有效性检查:

资源 退货 基本原理
(spec/valid? ::person {
    ::first-name "John"
    ::last-name "Doe"
    ::birthdate (LocalDate/of 1970 1 1)})

true

(spec/valid? ::person "f")

false

string is not a map

(spec/valid? ::person {
    ::last-name "Doe"
    ::birthdate (LocalDate/of 1970 1 1)})

false

map doesn’t contain a value under the ::first-name key

(spec/valid? ::person {
    ::first-name "John"
    ::last-name "Doe"})

true

birthdate is not required

(spec/valid? ::person {
    ::first-name "John"
    ::last-name "Doe"
    ::birthdate "Unknown"})

false

birthdate is not a LocalDate

(spec/valid? ::person {
    ::first-name "John"
    ::last-name "Doe"
    ::birthdate (LocalDate/of 1970 1 1)
    ::title "Mr"})

true

Additional entries are fine

规格集合

下一步是使用规范来验证集合中元素的类型,就像Java中的泛型一样, 例如 List<T>Map<T>Set<T> 。 这可以通过附加功能来实现:

  • coll-of为“标准”的Clojure集合, vectorlist等。
  • map-of地图

例如,指定LocalDate列表非常简单:

(spec/def ::dates (spec/coll-of ::date))

同样,对于keyword / LocalDate的映射:

(spec/def ::map-dates (spec/map-of keyword? ::date))

当然,它也适用于数据结构:

(spec/def ::map-persons (spec/map-of keyword? ::person))

结论

尽管Clojure是一种动态类型的语言,但可以通过使用spec库来补充类型。 与使用任何静态类型的语言一样,它允许验证简单的类型,枚举,映射和集合。

更进一步:

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

clojure

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值