web-dev-qa-db-ja.com

Scalaでシンボルを使用する実際的な例は?

Scalaには記号があります。名前は一重引用符で始まり、文字列定数の一種です。

Ruby(コロンで始まる)のシンボルを知っています。Rubyでは、ゲッターやセッターの生成など、いくつかのメタプログラミングタスクに使用されます。メンバー変数(たとえば、attr_reader :namename)のゲッターを生成します。

Scalaコードでのシンボルの使用はまだ多くありません。Scalaでのシンボルの実際の使用法は何ですか?

62
Jesper

シンボルは本当にScalaに適合しますか?

LISPのすばらしい土地では、コードは、それ自体を表すリテラルオブジェクト(文字列、数値など)のネストされたリストと、クラス、関数、変数などの識別子として使用されるシンボルとして表されます。 LISPコードは非常に単純な構造であるため、LISPを使用すると、プログラマーはコードを(コンパイル時と実行時の両方で)操作できます。明らかに、これを行うとき、プログラマーは必然的にデータオブジェクトとしてシンボルに遭遇します。

したがって、シンボルはいずれにせよLISPのオブジェクトである(そしてそうである必要がある)ので、ハッシュテーブルキーまたは列挙型としても使用しないのはなぜですか?これは自然なやり方であり、特別な列挙型を定義する必要がないため、言語をシンプルに保ちます。

要約すると、コードはコード操作、列挙、およびキーイングに自然に使用されます。しかし、Java人々はデフォルトでハッシュキー間の同値関係としてIDを使用しないため(従来のLISPはこれを行います)、キーとして文字列を使用できます。列挙型は、 Scalaそして最後に、データとしてのコードは言語によってまったくサポートされていません。

だから、私の印象は、シンボルがScala言語に属していないということです。それでも、この質問への返信には目を光らせていくつもりです。 Scalaでの記号の使用は、今は考えられません。

補遺: LISP方言によっては、LISPシンボルも名前空間で修飾される場合があります。これはもちろん、コードを操作するときに非常に便利な機能であり、文字列にはない機能です。)

33

Webで少し検索すると、(=言語のような)Scalaでの文字列との比較における記号(記号リテラル)の意味は、セマンティクスの問題であり、したがってコンパイラの認識でさえあります。

「文字列」は、一連の文字で構成されるデータ型です。文字列を操作して操作することができます。文字列は、ファイル名から画面に印刷されるメッセージ、CSVファイルの行など、意味的には任意のテキストデータにすることができます。

コンパイラの場合、つまりIDEの場合、文字列はデータ型Stringの値であり、数値(数字のシーケンス)はデータ型の値です。たとえば、整数です。プログラムレベルでは、「foo」と「bar」の間に違いはありません。

OTOHシンボルは識別子です。つまり、プログラム内のアイテムを意味的に識別します。この点では、それらはクラス名、メソッド名、または属性名のようなものです。しかし、クラス名はクラスを識別しますが、つまりクラスの構造と動作を宣言するプロパティのセット-およびメソッド名はメソッドを識別します-つまり、パラメータとステートメント-、シンボル名はシンボルを識別します-つまり。それ自体、それ以上のものはありません。

したがって、コンパイラは、クラスFooとBarを区別するように、シンボル 'fooと' barを明示的に区別できます。コンパイラのシンボルテーブルの一部として、IDEで同じメカニズムを適用して、たとえば、 'foo(つまり、このシンボルへの参照)の使用法を検索するには、クラスFoo。

比較すると、文字列「foo」を検索するには、全文スキャンなどのさまざまなアプローチが必要になります。これは、プログラムコード内の4711のすべての出現を検索するのと同じセマンティクスに従います。

それが私がそれを理解する方法です、私が間違っているなら誰かが私を訂正するかもしれません。

18
Det

Scalaの本によると、シンボルはインターンされています: "同じシンボルを2回書き込むと、両方の式はまったく同じSymbolオブジェクトを参照します。 = "

対照的に、Stringsは、リテラル形式で表示される場合にのみインターンされます(少なくとも、Javaは、Scalaについては完全にはわかりません)。したがって、多くのことを行うと思います。 Stringsをシリアル化してコレクションに入れると、代わりにシンボルを使用してメモリを節約できます。

しかし、私はスカフマンに同意します、私はそれらの使用に完全に確信していません。

(Rubyでは、Symbolsは、提供するメタプログラミングの例とは別に、Hashesのキーとしてよく使用されます。Rubyでは、これは便利です。 、Stringsがインターンされることはありません。すべてのStringが新しいメモリを割り当てます。Scalaで、前述したように、多くのメモリと組み合わせると便利です。 (de)serializationなので、Java Stringsもインターンされません。)

15
jqno

Scalaは関数型言語が使用しているため、追加したと思います。

しかし、彼らは、シンボルを介して識別子を参照する機能を追加することを忘れていました。これは、彼らの存在の中心点の一種です。 Scala 2.8には、その一部を提供する実験的な機能があります。APIドキュメントの関連部分を完全に引用します。


@experimental

object Invocation 
extends AnyRef

リフレクト呼び出しのより便利な構文。使用例:

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

次の2つの方法のいずれかを反映して呼び出すことができます。

    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になります。

著者ポール・フィリップス

15

シンボル間の比較の方が速いと思います。 Erlangを使用したことがあり、メッセージを渡すときにシンボルが1トン使用され、マシンの境界を越えてうまく機能する安価で高速なものが必要な場合。 IIRCのScalaでリモートアクターがどのような状態にあるかはわかりませんが、かなり危険でしたが、将来、リモートアクターが配置されると、Erlangとほぼ同じようにシンボルが非常に役立つ可能性があります。また、ケースクラスでは、いくつかの利点はそれほど明白ではありませんが、シンボルはさらに安価です。

5
Saem

コード内の既存の識別子ではないものの名前を参照したい場合に、それらを使用すると思います。 Scala本は、データベース列の名前を参照する例を示しています。これは任意の文字列ではなく、実際には事物の名前です。

それは少し希薄ですが、私は完全に確信していません。

3
skaffman

すでに述べたように、シンボルは他の(より多くの)関数型言語から引き継がれます。他の人が言及していないことは、それらがシンボルの役割を果たすだけでなく、キーワードに最も近い同等物でもあるということです(おそらくパフォーマンス上の利点を除いて)。私の意見では、これらは明示的な識別子を意味するキーワードとしてより有用です。

以下に、キーワードと記号の Clojure docs からの裁判所の説明を含めます。

シンボル

シンボルは、他の何かを参照するために通常使用される識別子です。これらをプログラム形式で使用して、関数パラメーター、letバインディング、クラス名、グローバル変数を参照できます。それらには名前とオプションの名前空間があり、どちらも文字列です。シンボルはメタデータを持つことができます(with-metaを参照)。

キーワード

キーワードは、それ自体に評価されるシンボリック識別子です。それらは非常に高速な同等性テストを提供します。シンボルと同様に、名前とオプションの名前空間があり、どちらも文字列です。先頭の「:」は名前空間または名前の一部ではありません。

Scalaシンボルは、一部の言語のシンボルほど強力ではありません。したがって、それらもあまり役に立ちません。ただし、キーワードと同じメタプログラミングとパフォーマンスの利点を提供できなかった理由はわかりません。少なくとも、コードを読みやすくすることができます。

Scalaでシンボルが実際に使用されているケースを1つ挙げることができます。 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)でシンボルの使用法を確認できます。シンボルの代わりに文字列リテラルを使用することも絶対に有効であり、一部の人はそうしていますが、anormソースコードでは、対応するメソッドシグネチャは実際にはシンボルパラメーターを使用しますタイプ。

1