在 Scala 中使用符号的实际例子?

Posted

技术标签:

【中文标题】在 Scala 中使用符号的实际例子?【英文标题】:Practical examples of using symbols in Scala? 【发布时间】:2010-11-22 09:46:24 【问题描述】:

Scala 有符号 - 名称以单引号 ' 开头,是一种字符串常量。

我知道 Ruby 中的符号(它们以冒号开头)。在 Ruby 中,它们用于一些元编程任务,例如为成员变量生成 getter 和 setter(例如 attr_reader :namename 生成 getter)。

我还没有看到在 Scala 代码中大量使用符号。 Scala 中符号的实际用途是什么?

【问题讨论】:

【参考方案1】:

我想当你想引用一个不是代码中现有标识符的事物的名称时,你会使用它们。 Scala 书中给出了引用数据库列名称的示例 - 它不是任意字符串,它实际上是事物的名称。

不过,这有点脆弱,我并不完全相信。

【讨论】:

【参考方案2】:

根据 Scala 书,Symbols 是实习的:“如果你写两次相同的符号,两个表达式将引用完全相同的Symbol 对象。

相比之下,Strings 仅在它们以文字形式出现时才会被实习(至少在 Java 中它们是,但对 Scala 并不完全确定)。所以我想如果你对Strings 进行大量序列化,然后将它们放入集合中,你可能会使用符号来代替并节省一些内存。

但我同意 skaffman 的观点,我并不完全相信它们的用途。

(在 Ruby 中,Symbols 除了您给出的元编程示例外,通常用作 Hashes 中的键。在 Ruby 中,这很有用,因为在那里,Strings 永远不会被实习:每个String 分配新内存。正如我所提到的,在 Scala 中它可能很有用,如果你将它与大量(反)序列化结合起来,那么 Java Strings 也不会被实习。)

【讨论】:

但是如果你使用字符串文字而不是符号文字,这是问题的重点,那么两者都会被实习。 没错,这就是序列化很重要的原因。它会为相同的字符串提供不同的对象,而 Symbols(据说)不会。此外,问题没有明确指定符号文字。如果它是你想要的文字,我的回答确实并不适用。我想,这会使符号更加脆弱:)。 Symbols 的自动实习还意味着 Symbols 的 equals() 可以实现为引用相等检查,因此比较可以非常快(而不是比较两个字符串的每个字符)。这意味着它们在许多用途上比字符串更有效,例如当用作 Map 中的键时? 注意:你可以像在 Java 中使用 "asdf".intern() 从字符串中获取实习生。 反序列化真的很重要。符号反序列化呢?保留身份似乎很重要,但您如何做到这一点?没有任何内容讨论 Symbold 序列化,尽管它至关重要,IMO。【参考方案3】:

我相信符号之间的比较更快。如果您使用过 Erlang,则在传递消息时会使用大量符号,并且您想要便宜、快速、跨机器边界运行良好的东西。我不确定远程演员在 Scala 和 IIRC 中处于什么状态,它们相当狡猾,但是将来当它们到位时,符号很可能会非常有用,就像它们在 Erlang 中一样。还有案例类,一些好处并不那么明显,但同样,符号仍然更便宜。

【讨论】:

【参考方案4】:

符号真的适合 Scala 吗?

在美妙的 Lisp 领域,代码被表示为表示自身的文字对象(字符串、数字等)和符号的嵌套列表,符号用作类、函数和变量等事物的标识符。由于 Lisp 代码具有非常简单的结构,Lisp 允许程序员操作它(在编译时和运行时)。显然,在这样做的时候,程序员不可避免地会遇到符号作为数据对象。

所以符号在任何情况下都是(并且必须是)Lisp 中的对象,那么为什么不将它们用作哈希表键或枚举呢?这是做事的自然方式,它使语言保持简单,因为您不必定义特殊的枚举类型。

总而言之,符号自然地用于代码操作、枚举和键控。但是 Java 人默认不使用身份作为哈希键之间的等价关系(您的传统 Lisp 就是这样做的),因此他们可以只使用字符串作为键。枚举类型在 Scala 中单独定义。最后,该语言根本不支持将代码作为数据。

所以不,我的印象是符号不属于 Scala 语言。也就是说,我将密切关注对这个问题的答复。他们可能仍然展示了在 Scala 中对符号的真正使用,这是我现在想不到的。

(附录: 根据 Lisp 方言,Lisp 符号也可能是命名空间限定的,这当然是在操作代码时非常有用的功能,而字符串则不具备这样的功能有。)

【讨论】:

您仍然希望在字符串上使用符号来键入哈希值,或者不这样做,因为它们更便宜并且更容易重构,强调后者。符号(如果有的话)促进代码和数据的分离,这在 Scala 中很受追捧,正如您清楚地说的那样,这不是一些在代码库中碰巧命名相同的魔术字符串,但它实际上是代码。 @Saem 你对代码和数据是正确的,但是,唉,在这方面,case 对象几乎每次都会击败符号。请参阅我对有关 Scala 中类型安全枚举的问题的回答。【参考方案5】:

我猜 Scala 添加它们是因为函数式语言使用它们。

但是,他们忘记了添加通过符号引用标识符的能力,这是他们存在的中心点。 Scala 2.8 中有一个实验性功能可以提供其中的一些功能。我将完整引用 API 文档的相关部分:


@experimental

object Invocation 
extends AnyRef

一种更方便的反射调用语法。 示例用法:

    class Obj  private def foo(x: Int, y: String): Long = x + y.length 

您可以通过以下两种方式之一进行反思:

    import scala.reflect.Invocation._
    (new Obj) o 'foo(5, "abc")                 // The 'o' method returns Any
    val x: Long = (new Obj) oo 'foo(5, "abc")  // The 'oo' method casts to expected type.

如果您调用oo 方法并且没有给类型推断器足够的帮助,它很可能会推断Nothing,这将导致ClassCastException

作者 保罗·菲利普斯

【讨论】:

【参考方案6】:

在网上搜索了一下,似乎 Scala(类似语言)中的符号(符号文字)与字符串相比是语义问题,因此甚至可能是编译器意识问题。

'String' 是一种数据类型,由一系列字符组成。您可以对字符串进行操作,从而对它们进行操作。字符串在语义上可以是任何文本数据,从文件名到要在屏幕上打印的消息、CSV 文件中的一行,等等。

对于编译器 - 因此 IDE - 字符串是数据类型 String 的值,就像数字(数字序列)是数据类型的值,比如:Integer。 “foo”和“bar”在程序层面没有区别。

OTOH 符号是标识符,即在语义上标识程序中的项目。 在这个问题上,它们就像类名、方法名或属性名。 但是,虽然类名标识了类-即声明类的结构和行为的一组属性 - 以及方法名称标识方法 - 即参数和语句-,符号名称标识符号-即本身,仅此而已-。

所以编译器可以明确区分符号'foo 和'bar,就像他区分类Foo 和Bar。作为编译器符号表的一部分,您可以在 IDE 中应用相同的机制,例如搜索 'foo 的用法(即对该符号的引用),就像您搜索类 Foo 的用法一样。

相比之下,搜索字符串“foo”需要不同的方法,例如全文扫描。它遵循与在程序代码中搜索所有出现的 4711 相同的语义。

我是这么理解的,如果我错了,有人可以纠正我。

【讨论】:

【参考方案7】:

我可以举出一种在 Scala 中真正使用符号的情况。 Play 2.2 使用 anorm 来访问数据库。 下面是简单的添加实体方法的代码示例:

def add(e:Entity): Option[Long] = 
    DB.withConnection implicit conn =>
        SQL("insert into ENTITY(name, description) values(name, description)").on('name -> e.name, 'description -> e.description).executeInsert()
    

所以你可以在.on(bla bla bla)中看到符号的用法 使用字符串文字代替符号也是绝对有效的,有些人正在这样做,但是在异常源代码中,相应的方法签名确实使用符号参数类型。

【讨论】:

【参考方案8】:

如前所述,符号从其他(更多)函数式语言中继承。其他人没有提到的是,它们不仅填补了符号的作用,而且它们也是最接近关键字的等价物(可能减去性能优势)。在我看来,它们作为关键字更有用,意味着明确的标识符。

下面我将包含来自Clojure docs 的关键字和符号的法庭描述。

符号

符号是通常用来指代其他事物的标识符。它们可以在程序形式中用于引用函数参数、let 绑定、类名和全局变量。它们有名称和可选的命名空间,两者都是字符串。符号可以有元数据(参见 with-meta)。

关键词

关键字是对自身求值的符号标识符。它们提供了非常快速的相等性测试。像符号一样,它们有名称和可选的命名空间,两者都是字符串。前导 ':' 不是命名空间或名称的一部分。

Scala 符号不如某些语言中的符号强大。因此,它们也没有那么有用。但是,我不明白为什么它们不能提供与关键字相同的元编程和性能优势。至少,它们可以使您的代码更易于阅读。

【讨论】:

以上是关于在 Scala 中使用符号的实际例子?的主要内容,如果未能解决你的问题,请参考以下文章

Scala的那些奇怪的符号 [+T]和[-T]

Scala的符号入门

为啥 Scala 在类名的末尾放置一个美元符号?

为啥 Scala 在类名的末尾放置一个美元符号?

是否有类似于 Haskell 的 $(美元符号)的 Scala 运算符?

scala中常用特殊符号