Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 实现数据模型以防止常见错误_Clojure_Datamodel - Fatal编程技术网

Clojure 实现数据模型以防止常见错误

Clojure 实现数据模型以防止常见错误,clojure,datamodel,Clojure,Datamodel,在Clojure中实现数据模型似乎有多种方法: 普通内置数据类型(映射/列表/集合/向量) 内置数据类型+元数据——例如:(类型^{:type::mytype}{:fieldname 1}) 内置数据类型+特殊访问器函数(例如,get从映射中提取不存在的密钥会引发异常,而不是静默返回nil) deftype 解构 解除记录 解除协议 我们已经到了映射/列表不再适用于我们的地步——我们遇到了许多前置条件/后置条件很容易捕捉到的错误,但要找出其他错误需要很长时间(并且很难为接受嵌套映射/列表/向

在Clojure中实现数据模型似乎有多种方法:

  • 普通内置数据类型(映射/列表/集合/向量)
  • 内置数据类型+元数据——例如:
    (类型^{:type::mytype}{:fieldname 1})
  • 内置数据类型+特殊访问器函数(例如,
    get
    从映射中提取不存在的密钥会引发异常,而不是静默返回
    nil
  • deftype
  • 解构
  • 解除记录
  • 解除协议
我们已经到了映射/列表不再适用于我们的地步——我们遇到了许多前置条件/后置条件很容易捕捉到的错误,但要找出其他错误需要很长时间(并且很难为接受嵌套映射/列表/向量的函数编写有效的前置/后置条件)--但我们不确定从上面哪一项中选择

我们有三个主要目标:

  • 编写习惯的Clojure代码
  • 避免花费大量时间查找愚蠢的类型错误
  • 相信我们能够通过无声地破坏任何东西来更改/重构代码

我们如何利用Clojure的强大功能来帮助我们呢?

能够编写在映射和列表上工作的函数真的很方便,如果通过切换到类和协议来实现这一点,那将是一种耻辱。毕竟,一种类型上有一百个函数更好。切换到协议或记录会有点笨手笨脚。例如,它会阻止您在调试时执行
(调试(映射:行(获取状态))

元数据是一种添加“刚好足够类型”的好方法,它可以使您的数据在需要它的地方更安全,而不会失去代码库其余部分的好处

  • '内置数据类型+元数据((类型^{:type::mytype}{:fieldname 1}))'

Clojure文化强烈支持原始数据类型。这是有道理的。但显式类型可能很有用。当普通数据类型变得足够复杂和特定时,基本上就有一个没有规范的隐式数据类型

依赖构造函数。从面向对象的角度来看,这听起来有点脏,但构造函数不过是一个安全方便地创建数据类型的函数。简单数据结构的一个缺点是它们鼓励动态创建数据。因此,与其调用(myconstructor…),不如调用它,我尝试直接组合数据。如果需要更改基础数据类型,则可能会出现错误和问题

记录是最佳选择。尽管对原始数据类型大惊小怪,但很容易忘记记录做了很多映射可以做的事情。它们可以以相同的方式访问。您可以对它们调用seq。您可以以相同的方式对其进行分解。绝大多数期望映射的函数也会接受记录

元数据救不了你。我对依赖元数据的主要反对意见是,它没有反映在平等中

user> (= (with-meta [1 2 3] {:type :A})  (with-meta [1 2 3] {:type :B}))
true
这是否可以接受取决于您,但我担心这会引入新的微妙的bug


其他数据类型选项:

  • deftype仅用于创建新的基本或特殊用途数据结构的低级工作。与defrecord不同,它不会带来clojure的所有优点。对于大多数工作来说,它不是必需的,也不是建议的
  • 当Rich Hickey引入类型和协议时,他本质上说应该永远首选defrecord

协议非常有用,即使它们感觉有点偏离(函数+数据)范例。如果发现自己创建了记录,也应该考虑定义协议。


编辑:我发现了简单数据类型的另一个优势,这在我之前并不明显:如果你在做web编程,那么简单数据类型可以高效轻松地转换成JSON(用于转换的库包括clojure.data.JSON、clj JSON和我最喜欢的cheshire).对于记录和数据类型,任务要烦人得多。

一个Alan Perlis ism不是在一个数据类型上有大约100个函数吗?我在标题中重新格式化了这个例子--不小心把它放在了非LISP括号中!是的,这就是我要说的,信用在哪里?好的,所以我需要弄清楚
defrecord
defprotocol
,可以忽略
defstruct
,不必太担心
deftype
。对于clojure程序来说,
defrecord
创建java代码有什么关系吗?从某种意义上说,我不想担心拥有java类,但如果clojure想私下使用一个,那就好了?太好了回答,非常简洁。正如您在上所看到的,普通映射也是一个java类,所以defrecord创建java类并不是什么大问题。从我们使用记录的角度来看,它不会有太大的区别——除了构造,它会感觉完全像clojure数据。是的,一定要阅读defrecord Defa协议nd扩展协议。它们背后的逻辑是经过深思熟虑的,我担心的是我不知道这到底意味着什么:
在与当前名称空间、给定字段以及可选的协议和/或接口方法同名的包中,为具有给定名称的类动态生成编译字节码。
(来自defrecord文档)