こんにちは、hachi8833です。
- リポジトリ: goby-lang/goby
- 公式サイト: https://goby-lang.org/
本記事ではGoby 0.1.9を用いています。1.0になるまでは仕様変更の可能性もありますのでご了承ください。
インストール方法や動かし方については第1回目をご覧ください。
注: GobyとRubyのどちらでも動くコードの冒頭には# Goby/Ruby
と、Gobyでのみ動くコードには# Goby
と表記することにします。
⚓Gobyの文法上の特徴(6)ブロックはdo
-end
のみ
Rubyのブロックはdo
-end
と { }
の2とおりの書式があります。
Gobyでは後者を廃止してハッシュリテラル専用とし、ブロックはdo
-end
(if
-end
なども含めてですが)のみで記述します。
# Goby
def foo(k)
b = "4".to_i
(1..k).each do |x|
b += x
end
b
end
foo(2)
#» 7
スタイル統一もさることながら、パーサーが圧倒的に書きやすくなるからだろうと推測しています。
// https://github.com/goby-lang/goby/blob/master/compiler/parser/expression_parsing.go#L204
func (p *Parser) parseGetBlockExpression() ast.Expression {
return &ast.GetBlockExpression{BaseNode: &ast.BaseNode{Token: p.curToken}}
}
⚓Gobyの文法上の特徴(7)Block
オブジェクトとget_block
キーワード
⚓Block
オブジェクト
Rubyのブロックは例外的にオブジェクトではないので、Proc.new
またはlambda
を用いてブロックをオブジェクト化します。Proc.new
とlambda
の挙動は微妙に違っているそうです。また、lambda
には->
というショートハンドが用意されてます。こうしたものは「無名関数(anonymous function)」と呼ばれることもあります。
参考: Rubyの ブロック、Proc.new、lambdaの違い - Qiita
Gobyのdo
-end
ブロックがオブジェクトではないという点はRubyと同様ですが、GobyではそのものズバリのBlock.new
にdo
-end
ブロックを渡すことでブロックオブジェクト化できます。
# Goby
b = Block.new do # bにBlockオブジェクトが保存される
100
end
b.call
#» 100
ブロックオブジェクトに対して#call
するとブロックが実行される点もRubyと同じです。#to_proc
的なメソッドはGobyにはありません。
Proc
やlambda
といった概念を取り入れていないので、その分Gobyは私にとってわかりやすいです😊。
⚓get_block
キーワード
Goby独自のキーワードとしてget_block
があります。
Rubyでは、ブロックをそのまま渡されたメソッドがそのブロックに対して行えるのはyield
のみです。そのため、メソッド定義のパラメータに&
を追加する(パラメータ名はblock
とする慣習)ことでブロックをProc
オブジェクトとして受けるしくみも用意されています。&
は引数を渡すときに付けることもできます。
# Ruby
[3] pry(main)> def foo(&block)
[3] pry(main)* block.call
[3] pry(main)* end
=> :foo
[4] pry(main)> foo do
[4] pry(main)* "hello"
[4] pry(main)* "bar"
[4] pry(main)* end
=> "bar"
Gobyでは、ブロック演算子&
を導入すべきかどうか検討した末、渡されたブロックリをget_block
キーワードでBlock
オブジェクトに変換できるという一種チート的な構文を代わりに採用しました。
# Goby
def foo
get_block # 受け取ったブロックをBlockオブジェクトにして返す
end
a = foo do # aにBlockオブジェクトが渡る
"hello"
"bar"
end
a.call
#» "bar"
もちろんRubyと同様、yield
もGobyで使えます。
# Goby/Ruby
def foo
yield 9
end
foo do |i|
i * 7
end
#» 63
get_block
は割りと軽いノリで導入された感じでした。おかげで記号がひとつ減ってうれしいです😊。
⚓参考: Gobyの用語について
一般にパラメータ(parameters)と引数(arguments)という用語は割りとごっちゃに使われることが多くてつらいので、Gobyのドキュメントでは厳密に区別するようにしています(少なくとも私は)。
- パラメータ: メソッド定義側に記述するもの
- 引数: メソッド呼び出し側に記述するもの
⚓Gobyの文法上の特徴(8)then
がない
Rubyスタイルガイドでもthen
は、if
やunless
が複数行の場合は使わないとあるように、原則使われなくなっています。使うときがあるとすればcase
文で使う場合があるとか、後はワンライナーで書くときぐらいでしょうか。
Gobyではthen
キーワードを取っ払いました。
# Goby
if true then
99
end
#» UndefinedMethodError: Undefined Method 'then' for <Instance of: Object>
⚓Gobyの文法上の特徴(9)メソッド名末尾に?
は置けるが!
は置けない
Rubyでは、述語メソッド(true
かfalse
のみを返すメソッド)の末尾には?
を、破壊的メソッド(レシーバーが改変されるメソッド)の末尾には!
を付ける慣習があります。
?
は実にシンプルかつ明確かつ有用で、何のあいまいさもありません。これは偉大な発明だと思います。
しかし!
の方は、「!
なしのメソッド」と「!
ありのメソッド」の両方がなければならないという慣習がなかなか徹底できずに「!
ありメソッド」しかないコードになったりとか、「破壊的」の解釈が人それぞれ違ったりと運用上混乱を招いてる面があります。この問題が「プリマドンナメソッド」と呼ばれるものです。
Gobyでは、メソッド名末尾の?
のみを許容し、!
は採用しませんでした。
# Goby/Ruby
class Foo
def found?
true
end
end
Foo.new.found?
#» true
⚓最近のGoby
⚓@st0012さんがRubyKaigi 2018に再び登壇
Gobyの作者@st0012さんが昨年のRubyKaigi 2017に続き、今年のRubyKaigi 2018にも登壇します。私もちょっぴりスライドをお手伝いしました。
⚓GobyにRustの血も入る?
GobyはRubyの文法をベースに、Go言語そのものの特徴も少し取り入れられていますが、いよいよRustの血も入りそうな流れです。
Gobyのエラーハンドリングをどうするかという議論がGobyのSlackで進められていて、@st0012さんはWikipediaの例外処理に対する批判を元に、例外によるエラーハンドリングは用いないことを決心しました。
それに代わるものとして、当初Go言語のような複数戻り値による原始的なエラーハンドリングも検討されたのですが、Rustのor_else
的な方法はどうかという提案が出て一気に盛り上がっています。
# Goby
File.open("/tmp/test.txt") do |f|
puts f.read
end.or do |err| # このend.orでエラー処理のブロックを記述
puts(err)
end
上はまったくの下書きですが、その後議論を経てOption
というエラーを含む結果の保持用クラスが提案され、Result
クラスにリネームされてついこの間マージされました。
その過程で、まさかの●●●メソッドが導入されて私は心底びっくりしました😮。その手があったかと。私もまだ全貌がわかっていませんし、今後どう変わるもわかりませんが。
詳しくは@st0012さんのセッションをお楽しみに😊。スライドが最終的にどうなるか私も楽しみです。