各言語における「symbol」はそれぞれ違う件（Ruby, JavaScript, TypeScript, Dart）

# はじめに

色んなプログラミング言語を勉強していくと、違う言語においてある単語が似たようで実は違うものとして使われることがしばしばありますね。今回はその中で今回「**シンボル**」（**symbol**）というものに関して話します。

`Symbol`というクラスは**Ruby**でも、**JavaScript**と**TypeScript**でも、**Dart**でも存在しますが、それぞれの言語でかなり違う概念です。

実際にRubyとDartでのシンボルは大体似ているから、Ruby経験者ならDartのシンボルのことも簡単に理解できるかもしれません。

しかしJavaScriptでは全く別物です。DartがJavaScriptに似ているとよく言われていますが、シンボルに関しては全然違うので、混同してはいけないことです。

確かにDartはあまりRubyに似ていないが、シンボルの概念のことだけはRubyの影響を受けている可能性がありますね。

Dartは比較的に新しい言語で、最近スマホアプリの開発に使われるFlutterの言語として広く知られるようになったばかりで、こうやってDartとRubyを並べて比較するのはこの記事で初めてかもしれません。

なおPython、PHP、C、Fortran、[IDL](https://qiita.com/phyblas/items/c62e8e9b4cb51a4b9447)など私の知っている他の言語にはシンボルというものは聞いたことないので比較の対象外です。

シンボルと呼ばれるものが存在する言語は他にもありますが、私が使ったことないから詳しく説明できないので、ここでは割愛します。


# シンボルの宣言

まずはSymbolというクラスのオブジェクトの作り方です。どの言語でもオブジェクト指向言語でシンボルというのはオブジェクトの一種です。

以下シンボルオブジェクトを作成して、ある変数に入れる書き方です。
<table>
  <tr>
    <th style="text-align: center">言語</th>
    <th style="text-align: center">宣言の書き方</th>
  </tr>
  <tr></tr>
  <tr>
    <td><b>Ruby</b></td>
    <td>

```ruby
smbl = :shinboru
```
または
```ruby
smbl = "shinboru".to_sym
```
</td>
  </tr>
  <tr>
    <td><b>JavaScript</b><br>と<br><b>TypeScript</b></td>
    <td>

```javascript
let smbl = Symbol("shinboru")
```
</td>
  </tr>
  <tr>
    <td><b>Dart</b></td>
    <td>

```dart
var smbl = #shinboru;
```
または
```dart
var smbl = Symbol("shinboru");
```
</td>
  </tr>
</table>

次はこの4つの言語におけるこのシンボルの使い方と概念について説明します。


# Rubyのシンボル

Rubyではシンボルが文字列と似ています。違うのは、作る時に文字列が`" "`か`' '`で作られるのに対し、シンボルは`:`の後ろに置くことで作られるということです。
```ruby
smbl = :くるま # シンボルを作る
str = "くるま" # 文字列を作る
```

どれも文字を持っていて、`[]`で中身の文字を参照できます。
```ruby
smbl[0] # "く"
str[1] # "る"
```

同じ文字を持つシンボルは同じものと見做されますが、文字列とは別物扱い。
```ruby
smbl==:くるま # true
smbl=="くるま" # false
```

また、文字列の中の文字は変更できますが、シンボルの中の文字は変更できません。
```ruby
str[2] = "み"
str # "くるみ"
smbl[2] = "み" # NoMethodError: undefined method `[]=' for :くるま:Symbol
```

こんな似ているようで違うものが2つ存在して、使い分ける必要があって、混乱させることもありますね。これはRubyの難しさの一つとも言えます。

ただしこういう使い分けはパーフォーマンスに繋がります。シンボルは文字列ほど使い勝手がよくない一方、処理の速度が文字列より速いから。これはシンボルの存在する意味です。

文字列の方が一般的に使うが、シンボルを使う場合も多少あります。例えば実はRubyでのメソッド（関数）の名前は文字列ではなくシンボルです。

試しにクラスを作ってその中でメソッドを定義して`.instance_methods`でメソッドの名前を調べたら配列の中のシンボルとして現れます。
```ruby
class Kurasu
  def met_a
  end
  def met_b
  end
end

Kurasu.instance_methods(false) # [:met_a, :met_b]
```
なお、ここで`(false)`が継承したメソッドを含まないするためですが、もしなかったらいっぱい出てきます。
```ruby
[:met_a, :met_b, :instance_of?, :kind_of?, :is_a?, :tap, :public_send, :remove_instance_variable, :singleton_method, :instance_variable_set, :define_singleton_method, :method, :public_method, :extend, :to_enum, :enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze, :inspect, :object_id, :send, :display, :to_s, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variable_get, :instance_variables, :instance_variable_defined?, :!, :==, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]
```

本当にシンボルいっぱいありますね。

その他にシンボルは普段ハッシュ（javaScriptではオブジェクト、Dartではマップと呼ぶけど）のキーに使われます。

例えばこんなハッシュを作って使ってみます。
```ruby
hito = {"myouji": "Yagami", "namae": "Tsuki"}

hito.keys[0].class # Symbol
hito["myouji"] # nil
hito[:myouji] # "Yagami"
```

これが文字列だと勘違いして同じ文字列で参照しても何も出ません。

実際に基本的にRubyのハッシュの`:`ではなく`=>`が使われますが、上述の宣言は
```ruby
hito = {:myouji => "Yagami", :namae => "Tsuki"}
```
の略です。

もし普通の文字列をキーとして使いたいなら可能ですが、その場合こう書きます。
```ruby
hito = {"myouji" => "Yagami", "namae" => "Tsuki"}

hito["myouji"] # "Yagami"
hito.keys[0].class # String
```

`JSON.parse`メソッドでjsonからハッシュに変換する時に普段キーは文字列となりますが、シンボルにして欲しい場合は`symbolize_names: true`を書きます。
```ruby
require 'json'

json = '{"hoge": {"fuya": "piyo"}}'
JSON.parse(json) # {"hoge"=>{"fuya"=>"piyo"}}
JSON.parse(json, symbolize_names: true) # {:hoge=>{:fuya=>"piyo"}}
```

普段文字列とシンボルは別のものとして扱われますが、どっちを使ってもいい場合もあります。

例えばRuby on Railsのコントローラのparamsです。普段はシンボルですが、文字列で参照しても問題ないです。なぜなら実質的にこのparamsはハッシュではなく、中身には文字列をシンボルと同一視させるという仕組みが含まれているから。普段のハッシュと混同してはいけません。

これについて詳しくはこの記事：

https://qiita.com/jnchito/items/74441837934ab8d006a1

このようにRubyでは色んなところでシンボルが活躍しています。

:::note info
### もっと読む
- [class Symbol (Rubyリファレンスマニュアル)](https://docs.ruby-lang.org/ja/latest/class/Symbol.html)
- [Rubyの文字列とシンボルの違いをキッチリ説明できる人になりたい](https://qiita.com/Kta-M/items/53a13ef60e14fcb41193)
- [Rubyのシンボルを丁寧に理解する](https://zenn.dev/kanoe/articles/352d78902c83e168db66)
- [Rubyにおけるシンボルの基本の使い方を徹底解説](https://style.potepan.com/articles/34196.html)
- [シンボルとは！？【Ruby超入門】](https://qiita.com/yyykms123/items/6a6ae7fe8cd9263a3d1c)
- [\[Ruby\] JSON相互変換](https://qiita.com/siakio/items/57c1a870c191eb10a0c0)
- [Hashのキーを文字列からシンボルに変換する](https://qiita.com/zakuroishikuro/items/5b08d65d4fef79982f19)
- [Rubyのハッシュのキーについて](https://qiita.com/beeytnh/items/e763abcbe3268256d2d8)
- [Rubyのハッシュとシンボル](https://qiita.com/ryosuketter/items/257d672eb83210b5f8dc)
- [Ruby シンボルについて](https://qiita.com/mofuko0213/items/c4b660325c54f29823fe)
- [\[Ruby\]シンボルとは何か。文字列との違いもはっきりさせたい。](https://qiita.com/chikachon_/items/2a2196d5c71edf2a7612)
- [Rubyで使えるメソッドの一覧を取得する](https://qiita.com/yahihi/items/f40f819a3ec5f81c08c3)
- [【Ruby】文字列とシンボルの違いについて](https://qiita.com/Hiro710/items/94156b934ba648743c71)
:::

# JavaScriptのシンボル

JavaScriptでは元々Symbolというクラスは存在しませんでした。このクラスが追加されたのはES (ECMAScript 2015)からです。だから出番は少なくて、文字列と混同することはあまりないでしょう。

JavaScriptのシンボルはRubyと全然違うもので、使い道も少ないです。使わなければならないという場合もないので、無理に使おうとしなくてもあまり問題ないでしょう。

JavaScriptでのシンボルはざっくり言うと「唯一無二の何か」を作るためです。同じようなシンボルを作っても同じものだと見做されません。
```javascript
let smbl1 = Symbol("シンボル")
let smbl2 = Symbol("シンボル")

alert(smbl1==smbl2) // false
alert(smbl1===smbl2) // false
alert(smbl1=="シンボル") // false
alert(smbl1==smbl1) // true
alert(smbl1===smbl1) // true
```

ただし直接その変数を代入したら流石に同じものだと認められますね。
```javascript
let smbl3 = smbl1
alert(smbl1==smbl3) // true
alert(smbl1===smbl3) // true
```

そんなシンボルの使い道は2つあります。まず何かの目的のために区別するオブジェクトとして使うのです。

例えばこんな分岐のある関数で使うです。
```javascript
const UDON = Symbol()
const SOBA = Symbol()
const RAAMEN = Symbol()

function chuumon(k) {
  if (k === UDON) {
    alert("うどんを注文します")
  }
  else if (k === SOBA) {
    alert("そばを注文します")
  }
  else {
    alert("らーめんを注文します")
  }
}

chuumon(UDON) // うどんを注文します
chuumon(SOBA) // そばを注文します
chuumon(RAAMEN) // らーめんを注文します
```

実際にはわざわざシンボルを使わなくてもただ違う数字など使ってもいいはずですね。
```javascript
const UDON = 0
const SOBA = 1
const RAAMEN = 2
```

ただしこの場合なら使う時にわざわざ変数を使わくなても単に数字を入れたらいいですね。
```javascript
chuumon(0) // うどんを注文します
chuumon(1) // そばを注文します
chuumon(2) // らーめんを注文します
```

実際にPythonなどシンボルの概念がない言語ではそうです。ここでシンボルを使うことでこの変数を使わなければならないということになります。

わざわざ数字ではなく変数を使うのは冗長に見えるかもしれませんが、これは可読性のためでもあるから、そう考えるとシンボルは役に立ちます。

もう一つの用途は、あるクラスのプライベートなプロパティを作るためです。

例えばこんなクラスを作ります。
```pkcls.js
let OMOSA = Symbol();
let TAKASA = Symbol();

class Pokemon {  
  constructor(namae, omosa, takasa){
    this.namae = namae;
    this[OMOSA] = omosa;
    this[TAKASA] = takasa;
  }
  
  jouhou(){
    alert(this.namae +"、重さ："+this[OMOSA] + "、高さ："+this[TAKASA]);
  }
}

export {Pokemon}
```

ここで重さと高さのプロパティはシンボルが使われますね。しかしシンボルはこのファイルの中で定義されて、exportされないので、外でこのクラスを使う時にこのプロパティにどうしようするすべはありません。

<ruby>鍵<rt>キー</rt></ruby>を持っていなくて勝手に作ることもできないから<ruby>入室<rt>アクセス</rt></ruby>できないということです。こうやって実質的にプライベートなプロパティとなります。

このクラスの使う例：
```javascript
import {Pokemon} from "./pkcls.js";

let kamonegi = new Pokemon("カモネギ", 15, 0.8);
kamonegi.jouhou(); // カモネギ、重さ：15、高さ：0.8
alert(kamonegi.namae); // カモネギ
```

こうやってメソッドを通じて重さと高さの情報は表示できますが、直接に重さと高さの値にアクセスすることはできません。ここで名前は普通のプロパティなので普通にアクセスできますが。

こんな風に、JavaScriptのシンボルはRubyのシンボルとは全く別物です。

:::note info
### もっと読む
- [Symbol (シンボル) - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Symbol)
- [JavaScriptのSymbolについて](https://qiita.com/hu-yu/items/55e4a94e38d6f46c1dbf)
- [JavascriptやTypescriptで登場するSymbol()って何だろう？と思って調べた話](https://nekoniki.com/ts-js-symbol)
- [【備忘録】Javascript : Symbol型について](https://qiita.com/noytdm1021/items/38d8e21e78689888ee5e)
- [シンボル(Symbol)って何？](https://qiita.com/lemon2003/items/55c947f29b44f79dc859)
- [JavaScriptでprivate「Symbol」編](https://qiita.com/schrosis/items/edfcd7e6a6ad59a36e08)
:::

# TypeScriptのシンボル

知っている人が多いと思いますが、実はTypeScriptはJavaScriptと殆ど似ていて、シンボルの概念もほぼJavaScriptと同じなのでここでは省略します。

:::note info
### もっと読む
- [symbol型 (シンボル型) | TypeScript入門『サバイバルTypeScript』](https://typescriptbook.jp/reference/values-types-variables/symbol)
- [【TypeScript】Symbolについて詳しく解説](https://marsquai.com/a70497b9-805e-40a9-855d-1826345ca65f/1dc3824a-2ab9-471f-ad58-6226a37245ce/b18ffd4a-6899-439a-9e94-cd456f6c5f75/)
- [TypeScriptでのSymbolとは？](https://qiita.com/yuyatuta/items/e10f90ac963d67dee2fd)
- [シンボル | TypeScript 日本語ハンドブック | js STUDIO](https://js.studio-kingdom.com/typescript/handbook/symbols)
- [TypeScriptの型について](https://qiita.com/Akihiro0711/items/80e9602fca3eafd0dc80)
- [TypeScript Handbook を読む (11. Symbols)](https://qiita.com/murank/items/04b3b31caa677750f240)
:::

# Dartのシンボル

Dartが生まれたのは2011年で、あの時JavaScriptにシンボルの機能（2015年から）はまだ正式に搭載されていないので、JavaScriptの影響をたくさん受けたもののDartのシンボルはJavaScriptのシンボルとは関係ないはずです。寧ろRubyのシンボルと似ています。

Dartではシンボルの概念はRubyと似て、「文字列と似ている何か」のことです。ただし文字列みたいに`[ ]`で文字を参照することはできません。

```dart
void main() {
  var smbl = #kuma;
  var str = "kuma";
  
  print(str); // kuma
  print(smbl); // Symbol("kuma")
  print(smbl == str); // false
  print(smbl == #kuma); // true

  print(str[1]); // u
  print(smbl[1]); // Error: The operator '[]' isn't defined for the class 'Symbol'.
}
```

文字列と同じく、普段どんな文字も使えますが、`#`で書く方法はラテン文字や数字しか使えないので、日本語などを使いたい場合`Symbol()`で作ります。

```dart
var smbl = Symbol("シンボル");
```

Rubyと同じように、実はDartの色んなところはシンボルによって成されています。例えば関数名、クラス名、変数名、全部ただの文字列ではなく、シンボルです。

それを見せるために`mirrors`という組み込みライブラリの`currentMirrorSystem()`関数を使って全てこの場で宣言されたものの名前を表示させます。
```dart
import 'dart:mirrors';

void kansuu() {}

class kurasu {}

int? hensuu;

void main() {
  currentMirrorSystem().isolate.rootLibrary.declarations.forEach((k, v) {
    print('$k: $v');
  });
}
```

```dart:結果
Symbol("kurasu"): ClassMirror on 'kurasu'
Symbol("hensuu"): VariableMirror on 'hensuu'
Symbol("kansuu"): MethodMirror on 'kansuu'
Symbol("main"): MethodMirror on 'main'
```

このように、宣言された関数（mainも含め）とクラスと変数の名前はシンボルで表示されます。

ただしRubyと違って、普段意識して使う場合は殆どないです。特にFlutterを使う時にシンボルに触れる場面はあまりないでしょう。だからググってもDartのシンボルに関する情報は少ないです。

シンボルの存在を知らなくても殆どの場合は普通にFlutterを使ってアプリを開発できるはずです。

Dartではシンボルがマップ（Rubyでのハッシュ、JavaScriptでのオブジェクト、Pythonでの辞書）のキーに使うこともできます。

シンボルをマップに使う必要がある場面は例えば、キーワード（名前付き引数）を持つ関数を`Function.apply`で呼び出す時です。

例えばこんな関数があります。
```dart
void shoukai(shainA, shainB, {kaisha, shokui}) {
  print('$shainAと$shainBは$kaishaの$shokuiです');
}
```
ここではkaishaとshokuiが`{ }`に囲まれているので、前のshainAとshainBみたいな位置引数ではなく、キーワードです。

使う時に普段はこう書きます。
```dart
void main() {
  shoukai(
    '松井K',
    '越水R',
    kaisha: '株式会社XXX',
    shokui: 'エンジニア',
  );
}
```
```:結果
松井Kと越水Rは株式会社XXXのエンジニアです
```

ただし、予め引数を持つリストとキーワードを持つマップを準備して`Function.apply`で呼び出すという方法もあります。その場合のマップのキーワードは文字列ではなく、シンボルです。
```dart
void main() {
  var argLis = ['松井K', '越水R'];
  var keyMap = {
    #kaisha: '株式会社XXX',
    #shokui: 'エンジニア',
  };
  Function.apply(shoukai, argLis, keyMap);
}
```

つまり関数キーワードの名前もシンボルです。

このようにシンボルと文字列の違いを意識して使う場合もありますが、Rubyと比べて出番が少なめです。

:::note info
### もっと読む
- [Dartシンボル(Symbol) - Florian Studio](https://www.ceodata.com/dart-symbol/)
- [関数のエミュレーション (Emulating functions)](https://www.cresc.co.jp/tech/java/Google_Dart2/language/emulating_functions/emulating_functions.html)
- [Dartの\`@\`から始まるMetadataを理解する](https://qiita.com/takyam/items/d1319f4686dea50523ff)
:::


# 終わりに
こんな感じで、以上4つの言語におけるシンボルの違いについて説明しました。

ちなみに似ているようで実は違うものは言語学で「空似言葉」（false friend）と呼ばれます。普段は自然言語において使われる概念ですが、プログラミング言語でも同じようなことがあるようですね。

逆に余談ですが、同じようなものなのに違う言語で違う呼び方をする場合もありますね。例えばこれです。
<table>
  <tr>
    <th style="text-align: center">言語</th>
    <th style="text-align: center" colspan="2">名称</th>
  </tr>
  <tr></tr>
  <tr>
    <td><b>Python</b></td>
    <td>辞書</td><td>Dictionary</td>
  </tr>
  <tr>
    <td><b>PHP</b></td>
    <td>連想配列</td><td>Associative Array</td>
  </tr>
  <tr>
    <td><b>Ruby</b></td>
    <td>ハッシュ</td><td>Hash</td>
  </tr>
  <tr>
    <td><b>JavaScript</b><br><b>TypeScript</b></td>
    <td>オブジェクト</td><td>Object</td>
  </tr>
  <tr>
    <td><b>Dart</b></td>
    <td>マップ</td><td>Map</td>
  </tr>
</table>

一般的に言えばどう呼べばいいでしょう？統一したらいいですね。

このように混乱を招くことが多いですが、それでも色んな言語の違いを比べることもやり甲斐があって楽しいことだと思いますね。