RubyKaigi 2023 イベントレポート

## はじめに
Rubykaigi2023にバーチャル参加をしたので、イベントレポートを書きます。
全体的に難しくてあまり理解ができていないので、sessionのレポートというよりも参加してみて初めて知った技術や印象的だったものについて概説します。

特に印象的だったのは、以下の3つです。
- JIT
- Ractor
- RBS

## JIT
```JIT```とは```Just In Time```の略称で、```実行時コンパイラ```のことを指します。
僕はRubyKaigiに参加するまでは、インタプリタの内部実装を意識したことがなかったので、```JIT```の存在すら知りませんでした。

JITコンパイラありのインタプリタがどう実行されるのかを簡単に書くと以下のようになります。
```
  プログラム
  
  ↓ (字句解析)

  トークン列
  
  ↓ (構文解析)

  AST

  ↓

  バイトコード(ネイティブの機械語ではない)

  ↓
  
  JITコンパイラによって実行直前にネイティブの機械語に変換される
```

Rubyに```JIT```自体が導入されたのはそれほど最近のことではなく、[Ruby2.6](https://www.ruby-lang.org/ja/news/2018/12/25/ruby-2-6-0-released/)からでした。
今回は、```Ruby3.2```から```YJIT```が実用段階になったということで盛り上がりを見せていました。

https://shopify.engineering/ruby-yjit-is-production-ready


インタプリタがどう動いているのかを手を動かして理解するには以下の書籍がとてもよかったです。(この書籍ではJITではなく、tree-walkingで実装されている)

https://www.oreilly.co.jp/books/9784873118222/

## Ractor
RactorはRuby3.0から導入された、並列処理のための仕組みです。
Ractorが登場するまでは、マルチスレッドによる処理は並行ではあっても並列ではありませんでした。
Ractorは並列に動作することを可能にするので、複数のスレッドが同時に別の処理を行うことができます。

```0 ~ 50000```それぞれに対して$\sum_{k=0}^n k$を求める繰り返し処理を例に考えてみます。
(※ 動作環境は、MacBook Air Retina, 13-inch, 2020 1.1 GHz クアッドコアIntel Core i5 メモリ8GBです。)

- 直列処理
```ruby
    require 'benchmark'
    
    def calc(i)
      ans = 0
      i.downto(0) do |j|
        ans += j
      end
    end
    
    Benchmark.bm do |x|
      x.report do
        (0..50000).each{|i| calc(i)}
      end
    end

    #      user     system      total        real
    # 53.037358   0.025490  53.062848 ( 53.215683)
```
- 並行処理
```ruby
    Benchmark.bm do |x|
      x.report do
        arr = [
          Thread.new { (0..12500).each{|i| calc(i)}},
          Thread.new { (12501..25000).each{|i| calc(i)}},
          Thread.new { (25001..37500).each{|i| calc(i)}},
          Thread.new { (37501..50000).each{|i| calc(i)}}
        ]
        arr.each(&:value)
      end
    end
    #      user     system      total        real
    # 56.438603   0.044576  56.483179 ( 56.575608)
```
- Ractorを用いた並列処理
```ruby
    Benchmark.bm do |x|
      x.report do
        arr = [
          Ractor.new { (0..12500).each{|i| calc(i)}},
          Ractor.new { (12501..25000).each{|i| calc(i)}},
          Ractor.new { (25001..37500).each{|i| calc(i)}},
          Ractor.new { (37501..50000).each{|i| calc(i)}}
        ]
        arr.each(&:take)
      end
    end

    #     user     system      total        real
    # 62.608054   0.042683  62.650737 ( 26.823120)
```
Ractorを使った並列処理の例がかなり早いことがわかります。

```
  b.rb:16: warning: Ractor is experimental, and the behavior may change in 
  future versions of Ruby! Also there are many implementation issues.
```
ただ、実行時に上記のような文章が出るように、Ractorはまだまだ改善段階のようです。

https://product.st.inc/entry/fukaborirubykaigi2022_vol2

https://techlife.cookpad.com/entry/2020/12/26/131858

https://atdot.net/~ko1/activities/2023_rubykaigi2023.pdf

## RBS
RBSは、Rubyの型について記述するための言語です。
Rubyの型付けは動的であるため型注釈をすることはありませんが、このRBSを使うことによって静的に型をつけることができます。

```lib/sample.rb
  class Person
    attr_reader :first_name, :last_name, :age
    
    def initialize(first_name:, last_name:, age:)
      @first_name = first_name
      @last_name = last_name
      @age = age
    end
    
    def full_name
      first_name + last_name
    end
  end
```

上記のようなコードの場合、以下のように型注釈をすることができます。

```sig/sample.rbs
  class Person
    attr_reader first_name: String
    attr_reader last_name: String
    attr_reader age: Integer
    def initialize: (first_name: String, last_name: String, age: Integer) -> void
    def full_name: -> String
  end
```

で、Rubyプログラムの型情報を書いただけではそんなに嬉しくないので、その型情報から型安全かをチェックします。
これはまた別の[steep](https://github.com/soutaro/steep)というgemが必要になります。

gemをインストールし、```steep init```をすると```Steepfile```が生成されます。
そこに以下のように記述すると```lib```ディレクトリの```Rubyプログラム```を```sig```ディレクトリの```rbsファイル```の型注釈に基づいてパースします。

```Steepfile
  target :lib do
    check "lib"
    signature "sig"
  end
```
```terminal
  $ steep check
```
現状のrbsファイルは正しく記述されているため、```No type error detected. 🫖```が出力されるはずです。
```lib/sample.rb
  # ...
  p = Person.new(first_name: "hello", last_name: "world", age: "1")
```
上記を追加し、再度```steep check```をすると以下のようなエラーが出力されます。
```terminal
  [error] Cannot pass a value of type `::String` as an argument of type `::Integer`
  │   ::String <: ::Integer
  │     ::Object <: ::Integer
  │       ::BasicObject <: ::Integer
  │
  │ Diagnostic ID: Ruby::ArgumentTypeMismatch
  │
  └ Person.new(first_name: "hello", last_name: "world", age: "1")
```
```age```が型注釈の```Integer```と異なっているため、弾かれていることがわかります。

https://techlife.cookpad.com/entry/2020/12/09/120454

## 終わりに
RubyKaigiのようなイベントに参加したのは初めてだったので、すごく刺激的でした。
内容はとても難しかったですが、Rubyの知見を深めるたくさんのきっかけを得ることができたと思っています。
予習をしていたらもっと楽しめたなという後悔があるので、来年は予習をして臨みたいです。

来年は沖縄で開催されるので、現地に行きたい！！