Ruby 为什么';哈斯克尔有符号(红宝石)/原子(厄尔朗)?

Ruby 为什么';哈斯克尔有符号(红宝石)/原子(厄尔朗)?,ruby,haskell,erlang,language-design,language-features,Ruby,Haskell,Erlang,Language Design,Language Features,我使用的两种语言是Ruby和Erlang,我一直认为它们非常有用 Haskell确实有代数数据类型,但我仍然认为符号会非常方便。我想到的一个直接用途是,由于符号与整数同构,所以可以在使用整数或字符串“主键”的地方使用它们 原子的语法糖可以是次要的-:某物或是原子。所有原子都是名为Atom的类型的实例,该类型派生Show和Eq。例如,您可以将其用于更具描述性的错误代码 type ErrorCode = Atom type Message = String data Error = Error Er

我使用的两种语言是Ruby和Erlang,我一直认为它们非常有用

Haskell确实有代数数据类型,但我仍然认为符号会非常方便。我想到的一个直接用途是,由于符号与整数同构,所以可以在使用整数或字符串“主键”的地方使用它们

原子的语法糖可以是次要的-:某物或是原子。所有原子都是名为Atom的类型的实例,该类型派生Show和Eq。例如,您可以将其用于更具描述性的错误代码

type ErrorCode = Atom
type Message = String
data Error = Error ErrorCode Message
loginError = Error :redirect "Please login first"
在这种情况下:重定向比使用字符串(“重定向”)更有效,并且比整数(404)更容易理解

这一好处似乎微不足道,但我认为值得添加atoms作为一种语言特性(或至少是GHC扩展)


那么,为什么语言中没有添加符号呢?或者我的想法是错误的?

原子不是由语言提供的,但可以作为一个库合理地实现:


hackage上还有一些其他的lib,但这一个看起来是最新的,并且维护得很好。

我认为最简单的答案是,在Lisp风格的符号(我相信这是Ruby和Erlang都想到的)中,Haskell中的大多数符号是:

  • 已经以其他方式完成了——例如,一个数据类型有一堆空构造函数,它们也可以作为“方便的整数名称”

  • 难以适应——存在于语言语法级别而不是常规数据的东西通常有更多与其相关的类型信息,但符号必须是彼此不同的类型(如果没有某种轻量级的特殊求和类型,几乎毫无用处)或所有相同的类型(在这种情况下,它们与仅使用字符串几乎没有区别)

另外,请记住,Haskell本身实际上是一种非常非常小的语言。很少有“烘焙”的东西,而且大多数东西只是其他原语的语法糖。如果包含一堆GHC扩展,这就不那么正确了,但是GHC with-xandtheKitchenskink也不是Haskell本身的语言

此外,Haskell非常容易接受伪语法和元编程,因此即使没有内置它,也可以做很多事情。特别是当你进入TH和Thrable类型的元编程以及其他任何领域时

因此,归根结底,符号的大部分实用功能已经可以从其他功能中获得,而不可用的东西将难以添加

我想到的一个直接用途是,由于符号与整数同构,所以可以在使用整数或字符串“主键”的地方使用它们

改用
Enum

data FileType = GZipped | BZipped | Plain
  deriving Enum

descr ft  =  ["compressed with gzip",
              "compressed with bzip2",
              "uncompressed"] !! fromEnum ft

Haskell使用类型构造函数*而不是符号,因此函数可以使用的符号集是封闭的,并且可以由类型系统进行推理。您可以向语言中添加符号,但这将使您处于使用字符串的相同位置-您必须在runtim中对照少数具有已知含义的符号检查所有可能的符号e、 添加各地的错误处理,等等。这将是所有编译时检查的一个大的解决方法

字符串和符号之间的主要区别在于内部-符号是原子的,可以在恒定时间内进行比较。但是,这两种类型都具有本质上无限多的不同值,这与Haskell使用有限类型指定参数和结果的粒度相反

  • 与Haskell相比,我更熟悉OCaml,因此“类型构造函数”可能不是正确的术语

我同意camccann的答案,即它可能缺失,主要是因为它必须深入到实现中,对于这种复杂程度的应用太少。在Erlang(以及Prolog和Lisp)中,符号(或原子)通常用作特殊的标记,并且与构造函数的概念基本相同。在Lisp中,动态环境包括编译器,因此它在一定程度上也是一个(有用的)编译器概念泄漏到运行时

问题在于,符号插入不纯正(它修改了符号表)。因为我们从不修改现有对象,所以它在引用上是透明的,但是如果实现得太简单,可能会导致运行时出现空间泄漏。事实上,正如目前在Erlang中实现的那样,实际上可以通过插入太多的符号/原子(我认为当前的限制是2^20)使VM崩溃,因为它们永远不会被垃圾回收。如果在符号表周围没有一个巨大的锁,在并发设置中也很难实现

但是,这两个问题都可以(并且已经)解决。例如,请参阅。我在软件包中使用此技术。它在引擎盖下使用
unsafePerformIO
,但仅在(希望)中使用极少数情况。它仍然可以使用GC的一些帮助来执行类似于间接缩短的优化。它还在内部使用了相当多的
IORef
s,这对性能和内存使用来说不是太大


总而言之,这是可以做到的,但是正确地实现它是非常重要的。编译器编写者总是权衡一个功能的强大与它的实现和维护工作,似乎一流的符号在这一点上失去了优势。

我很确定Erlang在Prolog中得到了这个想法,这可能受到Lisp的影响,但同样类似于拉斯曼斯:哦,好的观点。我不知道为什么我忘记了Prolog对Erlang的影响。这确实更有意义。Erlang肯定是从Prolog获得的,而Prolog是从Lisp获得的。添加原子似乎是对Haskell非常容易使用和实现的更改。而且,我可以想象它会导致