Tech Racho エンジニアの「?」を「!」に。
  • 開発

Goby: Rubyライクな言語(5)文字列編: 文字列とシンボルは同一、ハッシュキーはシンボルのみ、エンコードはUTF-8固定

こんにちは、hachi8833です。

本記事ではGoby 0.1.9を用いています。1.0になるまでは仕様変更の可能性もありますのでご了承ください。

インストール方法や動かし方については第1回目をご覧ください。

Goby: Rubyライクな言語(1)Gobyを動かしてみる

: GobyとRubyのどちらでも動くコードの冒頭には# Goby/Rubyと、Gobyでのみ動くコードには# Gobyと表記することにします。

Gobyの文法上の特徴(4)文字列とシンボルは同一

GobyにもRubyと同様に:symbolのようなシンボルリテラル表記があります。しかしGobyのシンボルリテラルは、単なる文字列リテラルのショートハンドでしかありません。従って、:symbolは常にStringクラスのオブジェクトである"symbol"に変換されます。

# Goby
:apple.class
#» String

fruits = [:apple, :orange, :grape, :banana]
#» ["apple", "orange", "grape", "banana"]
fruits[0].class
#» String

ご存知のとおり、RubyにはSymbolクラスがあります。GobyにはSymbolクラスはありません

# Ruby
[1] pry(main)> :symbol.class
=> Symbol
[2] pry(main)>

RubyではStringSymbolかを判定する一手間が生じがちで、RailsだとActiveSupport::HashWithIndifferentAccessで戦うことがよくありますが、Gobyはその点の解消を図っています。

Rubyのシンボルをなくせるか考えてみた(翻訳)

なお、Rubyにあるスペース含みの文字列をシンボルに変換する機能は、Gobyにはありません。スペース含みの文字列は普通に" "' 'で書きましょう。

# Goby
:"grape fruit"  # この書き方はできない
#» unexpected : Line: 0
# Ruby
[2] pry(main)> :"grape fruit"
=> :"grape fruit"

参考: 文字列のfreezeについて

現時点のGobyでは文字列のfreezeはまだ導入されていませんが、いずれ導入したいという意向です。また、破壊的メソッドは極力減らすか、場合によっては完全になくすかもしれません。

Gobyで文字列リテラルのobject_idを取ってみるとわかりますが、今は毎回オブジェクトIDが異なっています。

# Goby
"hello".object_id
#» 842353280544
"hello".object_id
#» 842350512064
"hello".object_id
#» 842353281344

もちろん、リテラルを変数に代入すればオブジェクトIDは定まります。

# Goby
str = "hello"
#» hello
str.object_id
#» 842353282208
str.object_id
#» 842353282208

なにしろ今のGobyはInteger型であっても数値リテラルのオブジェクトIDが一定ではないので(´・ω・`)。
最適化は今後の課題と割り切っています。

# Goby
1.object_id
#» 842350513056
1.object_id
#» 842353284864
1.object_id
#» 842350514080

Gobyの文法上の特徴(5)ハッシュリテラルのキーはシンボルリテラルのみ

Gobyのハッシュリテラル{ key: "value" }はRubyと同じ要領で扱えますが、1つ重要な違いがあります。ハッシュのキーに使えるのは事実上シンボルリテラルだけです。もう少し正確に言うと「シンボルリテラルに変換可能な文字列」ですね。

次のように変数aをこしらえてハッシュリテラルのキーに置くと、その場は通ってしまいますが、参照時に怒られます。Rubyのようにキーに一般的なオブジェクトを使うことはできません。

# Goby
» a = [1, 2, 3]
glocery = { apple: 1, orange: 2, a: 3 }
glocery[a]
#» TypeError: Expect argument to be String. got: Array

それだけではありません。次のように文字列リテラルをハッシュリテラルのキーとして使うことも許されません。キーは常にシンボルリテラルでなければなりません。

# Goby
glocery = { apple: 1, orange: 2, "grape": 3 }
#=> unexpected : Line: 0

当然、ロケット演算子=>もGobyにはありません😎。

この仕様を見返してみて、オブジェクトをハッシュリテラルのキーに指定する時点で怒るのがよさそうにも思えましたが、それをやると変数名とシンボルリテラルがたまたま同じになったときにまで怒られてしまい、間違いなくうざくなるので、この方法がよいのだと思います。

Gobyの文法上の特徴(6)文字列のエンコードはUTF-8一択

RubyではCSI方式に基づき、さまざまなエンコーディングを文字列オブジェクトに指定できます。マルチエンコーディングの扱いについてはRubyは最強の部類でしょう。

Rubyの内部文字コードはUTF-8ではない…だと…?!

GobyのStringクラスはそのあたりをすっぱり割り切って、UTF-8一択にしています。当初はGo言語の素朴なstringが文字列型に使われていたのですが、漢字などのマルチリンガル文字を正しくカウントできるようruneも導入し、4バイトのUTF-8に対応すべくテストにも寿司ビールチェックを追加してあります。runeはまだ徹底できてない気もするので、今後の課題かもしれません。

# https://github.com/goby-lang/goby/blob/54255ddc4b30b8daf6e5aad48e2aae2db8120aab/vm/string.go#L500
            Name: "capitalize",
            Fn: func(receiver Object, sourceLine int) builtinMethodBody {
                return func(t *Thread, args []Object, blockFrame *normalCallFrame) Object {

                    str := receiver.(*StringObject).value
                    start := string([]rune(str)[0])
                    rest := string([]rune(str)[1:])
                    result := strings.ToUpper(start) + strings.ToLower(rest)

                    return t.vm.initStringObject(result)
                }
            },

MySQLのencodingをutf8からutf8mb4に変更して寿司ビール問題に対応する

最近のGoby

GobyのAPIドキュメントを更新中

例の結城浩さんのツイートを意識してということもあり、最近ちまちまとGobyのAPIドキュメントやテストを更新するのが無上の楽しみです。

  • ArrayのAPIドキュメント修正: 3282176
  • BlockのAPIドキュメント修正: 1265f65
  • BooleanのAPIドキュメント修正: 83a2506

ドキュメントを更新してみると、思いもよらぬバグが見つかったりするのがまた楽しい😊。いっぺんに直すと迷惑になるので、ドキュメントにまずTODOでメモして、別プルリクで修正するようにしてます。

[1, 2, 3]みたいな味も素っ気もないサンプルコードも、そのうちもう少し血の通ったものにしたいと思います。

関連記事

Goby: Rubyライクな言語(2)Goby言語の全貌を一発で理解できる解説スライドを公開しました!


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。