有没有办法在Clojure中创建受约束的数据类型?

有没有办法在Clojure中创建受约束的数据类型?,clojure,Clojure,例如,一个仅包含有效电子邮件地址的字符串,如某些正则表达式所定义 如果此类型的字段是更复杂的数据结构的一部分,或用作函数参数,或在任何其他上下文中使用,则客户端代码将能够假定该字段是包含有效电子邮件地址的字符串。因此,不需要像“有效”这样的检查,所以这种方法不起作用 在Haskell中,这可以通过a和Java来实现,方法是确保类型是不可变的(所有setter都是私有的),并在构造函数中添加一个检查,如果用于创建类型的字符串不包含有效的电子邮件地址,该检查将引发RuntimeException 如

例如,一个仅包含有效电子邮件地址的字符串,如某些正则表达式所定义

如果此类型的字段是更复杂的数据结构的一部分,或用作函数参数,或在任何其他上下文中使用,则客户端代码将能够假定该字段是包含有效电子邮件地址的字符串。因此,不需要像“有效”这样的检查,所以这种方法不起作用

在Haskell中,这可以通过a和Java来实现,方法是确保类型是不可变的(所有setter都是私有的),并在构造函数中添加一个检查,如果用于创建类型的字符串不包含有效的电子邮件地址,该检查将引发RuntimeException


如果这在普通Clojure中是不可能的,那么我希望看到该语言的一些著名扩展中的示例实现,例如。

好的,也许,我现在理解了一个问题,我在评论中阐述了我的想法,但不是很好。因此,我试图为你的问题提出一个可接受的解决方案,然后我试图解释我在评论中试图表达的一些想法

1) 有一个为类生成编译字节码的
gen类
,您可以在那里为类设置构造函数

2) 您可以使用
defrecord
在项目中约定为私有的某个命名空间中创建
记录
,然后 使用公共api创建另一个命名空间,并在此处定义工厂函数。因此,公共名称空间的用户只能调用公共名称空间的公共函数。(当然,他也可以打私人电话,但需要另外一些密码)

3) 您只需定义一个函数,如
makeemail
,它将返回一个
map
。 所以您没有在任何地方指定数据结构

4) 您可以只记录代码,在其中警告人们使用factory函数进行构造

但是!在Java中,如果代码需要一些接口,那么给代码提供有效的接口实现是用户的问题。因此,如果您用Java编写一点点通用代码,您已经失去了有效电子邮件字符串的属性。这是因为Java是静态类型语言

Clojure通常是动态类型的,因此用户通常应该能够将任意数据结构传递给任意函数,而不会在编译时出现任何类型问题,如果传递错误的数据,这是他的错。例如,这使得这件事成为可能:创建一个
记录
并创建一个工厂(构造函数)函数。并且您希望在代码中传递
记录。但是用户可以使用与
记录
字段名称相同的键传递
映射
,代码将正常工作

所以,一般来说,如果您希望代码的用户负责以动态类型语言传递所需的类型,那么用户以您提供给他的正确方式负责构造它就不需要花费任何费用

另一种解决方案是:用户只需编写测试。您可以在api函数
:pre
:post
中指定条件来检查结构。您可以将键入的clojure与我上面写的想法结合使用。您可以使用一些附加的声明性库,就像@thumboil的第一条注释中提到的那样


另外,我不是clojure专业人士,因此我很容易错过一些更好的解决方案。

您看过吗?在Java中,如果您在构造函数中检查字符串,则不能保证字符串有效。您可以有数千个方法,它们会以某种方式更改类中的字符串。每次更改时,您都需要检查它是否有效。通常,您只有一个“checkString”方法,该方法在每个更改字符串的方法中按约定调用。所以,我看不出您所描述的内容和创建数据结构的函数之间的实际区别。您可以将其命名为“make email”,并在每次需要构造电子邮件数据结构时使用它。@我们假设受约束的字符串是不可变的(所有setter都是私有的)。因此,如果您想更改它,您不能这样做:您实际上获得的不是原始字符串的修改副本。要获取副本,必须调用其构造函数,其中包含检查。因此,我只需要在构造函数中编写一次检查。在Clojure中,我可以绕过“生成电子邮件”,直接创建该类型的无效实例。我想阻止它,否则在类型上操作的代码将不得不自己进行检查。在Java中,它是被阻止的。