Ruby 2.7.0-preview2がリリース

10/22に2.7.0-preview2がリリースされました🎉。
機能の多くは週刊Railsウォッチでも報じてきましたが、改めて覗いてみました。「Experimental」と銘打たれた機能が割と目に付きます。誤りがありましたら@hachi8833までお知らせください🙇。

Compaction GC

GC.compactでヒープを明示的に圧縮できます。

irb(main):006:0> GC.compact
=> {:considered=>{:T_NONE=>5736, :T_OBJECT=>258, :T_CLASS=>197, :T_MODULE=>23, :T_FLOAT=>0, :T_STRING=>5605, :T_REGEXP=>131, :T_ARRAY=>760, :T_HASH=>61, :T_STRUCT=>13, :T_BIGNUM=>0, :T_FILE=>0, :T_DATA=>177, :T_MATCH=>0, :T_COMPLEX=>0, :T_RATIONAL=>0, 16=>0, :T_NIL=>0, :T_TRUE=>0, :T_FALSE=>0, :T_SYMBOL=>8, :T_FIXNUM=>0, :T_UNDEF=>0, 23=>0, 24=>0, 25=>0, :T_IMEMO=>3979, :T_NODE=>0, :T_ICLASS=>30, :T_ZOMBIE=>0, :T_MOVED=>0}, :moved=>{:T_NONE=>0, :T_OBJECT=>258, :T_CLASS=>197, :T_MODULE=>23, :T_FLOAT=>0, :T_STRING=>5605, :T_REGEXP=>131, :T_ARRAY=>760, :T_HASH=>61, :T_STRUCT=>13, :T_BIGNUM=>0, :T_FILE=>0, :T_DATA=>177, :T_MATCH=>0, :T_COMPLEX=>0, :T_RATIONAL=>0, 16=>0, :T_NIL=>0, :T_TRUE=>0, :T_FALSE=>0, :T_SYMBOL=>0, :T_FIXNUM=>0, :T_UNDEF=>0, 23=>0, 24=>0, 25=>0, :T_IMEMO=>3979, :T_NODE=>0, :T_ICLASS=>30, :T_ZOMBIE=>0, :T_MOVED=>0}}

GC.compactは以下を実行します。

  1. フルGC
  2. オブジェクトを移動
  3. 参照更新
  4. フルGC

パターンマッチング(Experimental)

case構文でinを使えるようになりました。関数型言語で広く用いられている手法です。現在はwarningが表示されます。

case {a: 0, b: 1}
in {a: 0, x: 1}
  :unreachable
in {a: 0, b: var}
  p var #=> 1
end

# warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!

REPL(irb)の改良

これも既に有名ですね。色付けやオートインデント、Tabキー2度押しでAPIドキュメント表示、履歴をブロック単位で遡る機能などが追加されました。irbを終了しても履歴が保存されるようになったのがかなりありがたいです😂。

positional引数とキーワード引数の分離

Ruby 3.0でのbreaking changeに備え、Ruby 2.7で各種warningを表示します。まだ自分の中で未消化なので別記事にしようと思います🙇。

追記(2019/11/18)以下の翻訳記事を出しました。

Ruby 2.7: ハッシュからキーワード引数への自動変換が非推奨に(翻訳)

パフォーマンス改善

  • JITの改善(Experimental)
  • 以下の結果はfrozen stringになる
# シンボルのto_s
:symbol.to_s.frozen?  #=> true

# Module.name
module Bar ;end
Bar.name.frozen?      #=> true

# trueやfalseのto_s
true.to_s.frozen?     #=> true
false.to_s.frozen?    #=> true

その他追加された機能追加

メソッド参照演算子.:の追加(Experimental)

#method(:メソッド名)と同等です。メソッドを呼び出すのではなく、Methodオブジェクトを返します。

[1,2,3].map do |x| Math.method(:sqrt) end
# 上と下は同等
[1,2,3].map do |x| Math.:sqrt end

#=> [#<Method: Math.sqrt>, #<Method: Math.sqrt>, #<Method: Math.sqrt>]

これを用いて、ブロックとmethodなしで以下のように短く書けます。

[1,2,3].map(&Math.:sqrt)
#=> [1.0, 1.4142135623730951, 1.7320508075688772]

warningは表示されません。

追記(2019/11/18)その後メソッド参照演算子はひとまず取り消しになりました。

参考: Revert “Method reference operator” · ruby/ruby@fb6a489
参考: Feature #16275: Revert `.:` syntax - Ruby master - Ruby Issue Tracking System

numbered parameterの導入(Experimental)

デフォルトのブロックパラメータをショートハンドで表せます。表記法について議論の末、_1_2といった表記に落ち着いたようです。

[1, 2, 10].map do |i| i.to_s(16) end
# 上と下は同等
[1, 2, 10].map do _1.to_s(16) end

#=> ["1", "2", "a"]

warningは表示されません。

range ..の開始値の省略記法(Experimental)

Ruby 2.6で..の終了値の省略記法が導入されたのに続き、開始値も省略可能になりました。さすがに開始値と終了値を両方省略すると構文エラーになりました。

ary = %w(1, 2, 3, 4)
ary[..3]    # ary[0..3]と同等
#=> ["1,", "2,", "3,", "4"]

warningは表示されません。

Enumerable#tallyの追加

要素ごとの個数をハッシュで得られます。当初は#count_byという名前でした。

["a", "b", "c", "b"].tally
#=> {"a"=>1, "b"=>2, "c"=>1}

tally: (競技の)得点

レシーバーにselfを指定してprivateメソッドを呼べるようになった

インスタンスメソッドからprivateメソッドを呼び出すときにレシーバーにselfを指定してもエラーにならなくなりました。

# 従来
class Foo
  def foo
    self.baz  # レシーバーは指定できないのでエラー
  end
  def bar
    baz       # 呼べる
  end

  private

  def baz
    42
  end
end
Foo.new.foo
#=> NoMethodError (private method `bar' called for #<Foo:0x000055b4b7980258>)
Foo.new.bar
#=> 42
# 2.7
class Foo
  def foo
    self.baz  # selfならレシーバーとして指定できるので呼べる
  end
  def bar
    baz       # 呼べる
  end

  private

  def baz
    42
  end
end
Foo.new.foo #=> 42
Foo.new.bar #=> 42

従来のprivateメソッドは「関数形式でしか呼び出せない」つまり「呼び出しでレシーバーを指定できない」という概念でしたが、「呼び出しでself以外のレシーバーを指定できない」みたいに変わることになりそうですね。

参考: クラス/メソッドの定義 (Ruby 2.6.0)
参考: [Ruby] privateメソッドの本質とそれを理解するメリット - Qiita

Enumerator::Lazy#eagerを追加

Enumerator::LazyからEnumeratorを生成します。

a = %w(foo bar baz)
e = a.lazy.map {|x| x.upcase }.map {|x| x + "!" }
e.class #=> Enumerator::Lazy

e = a.lazy.map {|x| x.upcase }.map {|x| x + "!" }.eager
e.class #=> Enumerator

メソッドに渡されたブロックを(ブロックなしの)Proc.newprocで受けるとwarningを表示

これができていたこと自体初めて知りました😳。

def foo
  proc.call # またはProc.new.call
end
foo { puts "Hello" }
#=> warning: Capturing the given block using Proc.new is deprecated; use `&block` instead
#=> Hello

メッセージにあるようにブロック引数(&blockなど)か、yieldで従来どおりに受けられます。

# 従来どおり
def foo(&block)
  block.call
end
foo { puts "Hello" }  #=> Hello

def foo
  yield
end
foo { puts "Hello" }  #=> Hello

メソッドに渡されたブロックを(ブロックなしの)lambdaで受けるとArgumentErrorを表示

def foo
  lambda.call
end
foo { puts "Hello" }
#=> ArgumentError (tried to create Proc object without a block)

従来は以下のwarningが表示されていましたが、2.7で完全に禁止になります。

# warning: tried to create Proc object without a block
#=> Hello

Unicode v12の絵文字対応

Unicode version 12で追加された大量の絵文字に対応しました。Ruby 2.6系も対象になるとあります。

参考: Emoji List, v12.0 — 1,719個あります。

令和対応

なお#15195は2.6.3以降にも導入済みです。

"\u32FF"
#=> "㋿"

Date.new(2019, 5, 1).jisx0301
#=> "R01.05.01"

コンパイラをC99必須に、C90サポートを廃止

参考: C99 - Ruby master - Ruby Issue Tracking System

おたより発掘

関連記事

Ruby2.5.xのパラメータの制約についてまとめてみた

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の監修および半分程度を翻訳、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れて更新翻訳中。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好きで、Goで書かれたRubyライクなGoby言語のメンテナーでもある。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

夏のTechRachoフェア2019

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ