RBSコミッターになった

これまで

なぜかPRがmergeできるwrite権限を一昨年のクリスマスにもらっていた。 RBSリポジトリーのwrite権をRubyコミッター全員持っていた方がいいだろうという話題があがった時に、ついでにいただいた形だった。 この時点では、よくわからないけど貰えるものはもらっておこうの気持ちでいた。

さいきん

いつものようにPRを投げていたが、テストの修正とか小さいものも多い。これレビューするのも大変だろうなと思っていた。

そこで以前のミーティングで「小さい変更のPRなら適当にreviewしてmergeしておいて」と言われていたことを思い出した。 「それなら自分が送ったやつはapproveなしでmergeしてもいいのかな?」と疑問になったので聞いてみたら、良いとのことだったので流石にコントリビューターに権限与えすぎじゃないか?と思って聞いてみたらRBSコミッターと名乗って良いとのことだった。

これから

早くRubyでも型チェックが当たり前になれば良いのになーと思って活動しているので、これからもやっていきたい。

Vibe Coding時代に重要そうな基礎コーディング技術

Vibe Coding時代が気がついたら幕を上げてたのでとりあえず試してみた。

本記事では、基本的にAIエージェント用のチャット欄だけで全ての操作を行うことをVibe Codingと呼ぶことにする。

初めからプロダクトを作るのにもVibe Codingは適してそうだが、今回はもっと小さい単位の話。

プログラミングは結構めんどくさい。

「実装を変えたいけど同じパターンで複数書いてあるとめんどくさい」

「実装を変えたけどテストを書くのがめんどくさい」

「テストを走らせて修正して走らせてのサイクルがめんどくさい」

などのめんどくささといつも付き合うことになる。

「実装を変えたいけど同じパターンで複数書いてあるとめんどくさい」

同じパターンの繰り返しは、結構AIがTabを押したら解決してくれるようになってきた。これだけでもすごい。 変数名を変えたら空気読んで他の変数名も「ここも変えとこうか?」と(本当に言ってるわけではないが)サジェストしてくる。

「実装を変えたけどテストを書くのがめんどくさい」

この辺りになるとTabでは難しい。AIエージェントに状況を伝える。

「実装は修正したのでテストも変更したい。基本的な方針は〇〇〇でいいから、実装の変更に合わせてテストも修正して」

こんな感じでテストの修正がガガガと走る。気に入らなければ、

「やっぱりメソッドBとメソッドCはprivateメソッドとして、呼び出し元のメソッドAだけをpublicにしよっか。テストケースもBとCをAに統合して」

のような指示を出してる。(本記事はこの方法がいいというライフハック記事ではなく、私の個人的な日記である)

「テストを走らせて修正して走らせてのサイクルがめんどくさい」

コードを変更するたびに「テストを実行しましょうか?」と聞いてくるがいちいち「OK」と言うのもめんどくさい。

「聞かずにテストを実行していよ」と書いて許可する。この辺は設定で書いておけばいいのかもしれない。設定も自然言語だ。

Vibe Coding時代に重要そうな基礎コーディング技術

このような単位での変更の場合、自分が感じた重要な基礎コーディング技術は以下の3つだ。

  • Linting
  • Testing
  • Typing

Linting

AIエージェントが書いたコードは人間がレビューするとボトルネックになる。機械的にLintingして結果をAIエージェントが読んで「この指摘はこうすれば直ります」と独り言を言ってきて勝手に修正される。このLintingがしっかり指定してあれば、大きく違反したコードは出力されにくい。

Testing

AIが書いた実装はどう動くか分からないので、テストコードがAIの暴走を防ぐある種のセーフィティとなる。 AIの暴走を防ぐ、人類の最後の砦。それがテストコードなのだ。そのテストコードもAIに書いてもらうのだが。。。

Typing

型チェックもAIへの重要なヒントとなる。人間が指摘していては間に合わないので、機械的に型チェックツールがAIに指摘して修正してもらうと、ある程度の意図が伝わり暴走も食い止められる。

人間はボトルネックである

ここまで書いて再確認したが、Vibe Coding時代では人間が大きなボトルネックとなる。人間がコードを読んで書く時間は、AIとは比較にならないくらい遅い。常に自分がボトルネックであることを自覚しながらコーディングすることになる(誇張してます)。

AIは人間をサポートするためのツールである(今は)。人間がAIの教師データとなって、出力結果の成否を判断していく。コードは人間と機械との橋渡し的な中間出力となるのだろう。人間に読めない生成物は人間には成否が判断できない。しばらくはコードは必要なんじゃないかなあ。そうやって人間は人間にできることをやり、AIはAIにできることをやっていいく。いまはそういう時代なんだろう。

AIがAIの生成物の成否を判断して勝手に進化していくようになったら。。。それは今はSFの話だろう。今は。。。

つまり

AI時代でも型は重要だと思う。

コーヒーにハマっている

去年の2024年7月くらいからハンドドリップコーヒーにハマって、現在も続いているので現状を記録しておく。

ハンドドリップコーヒーとは

簡単に言うとコーヒーの粉にお湯を注いで抽出し、コーヒーを飲むという体験のこと。 店によってはフィルターコーヒーとかプアオーバーとか言ったりするやつ。 もっと言うと、コーヒーは粉ではなく豆から挽きたい。

きっかけ

同僚がアメリカのお土産に、スターバックス1号店の豆を買ってきてくれた。

これまでコーヒーはドリップバッグで1杯50円くらいのを飲んでいた。好きと言うほどでもないが、覚醒効果があって口寂しさも紛れるので1日2杯くらい飲む生活が何年か続いていた。ドリップバッグより手で淹れた方がパッケージやらのゴミも出なくて安くなるのでは?と何となくだけ思っていた。豆をもらったなら挽いて飲みたい。コーヒーを始める機運が出てきたのだ。

これまでのコーヒー観

コーヒーを淹れている時間が世界で一番時間がゆっくりになる。悪い意味で、である。 昔は正直コーヒーを淹れる時間はダルかった。お湯を注いでただ待つだけ。暇すぎる。かといって絶妙に他のことをしているほどの時間をおくと冷める。コーヒーはアツアツが一番美味しい。焦る気持ちを落ち着かせる必要があった。5秒が永遠に感じる。冷めたコーヒーはマズイので牛乳を入れて飲み干す。コーヒーはあくまで気つけ薬だった。

どうやって淹れるか

さてこのもらったコーヒー豆をどうするか。 最初に安易に浮かんだのがコーヒーメーカー。こんなにコーヒーを淹れるのがダルいのである。簡単であればあるほど良いだろうと思った。 豆を入れてスイッチを入れたらプロペラのグラインダーで挽かれ、ただ待っているだけでサーバーにコーヒーが溜まっているやつ。親戚も使っていたのでイメージもあった。

そんなイメージもありつつも、しかしまあそこまでするほどか?もしかしたらもっと手軽な方法もあるんじゃないか?

とりあえず他にどんな方法があるのか調べだした。

コーヒー系YouTuber

調べていくと、コーヒー系YouTuberがおもしろくていくつか観てみた。すると、ハンドドリップの情報がたくさん出てきた。 コーヒーメーカーは機械なのであまりネタが出せず、ハンドドリップはいくらでもやりようがあるのか、いろんな機材や方法論をYouTuberが熱く語るのである。 コーヒーを淹れるのに世界大会があるなんて知りもしなかった。この熱に当てられた気がする。 コーヒーを飲んでいるのにチョコレートだフルーツだいう言葉が出る。そんな大袈裟な。本当なの?コーヒーってそんなに幅があるものなの?

どうせならおもしろい方へ

結構迷ったが、ハンドドリップのコーヒーグラインダーをネットで注文した。 多くのYouTuberが「コスパ最強」と口々に言うTimemoreのC2だ。たしか9千円台だったと思う。 ドリッパーはなぜか家にあった。今思うと樹脂製のHARIO V60だったが、当時の自分にはドリッパーは同じ名前でも様々な素材バリエーションがあることも知らなかったのだ。ペーパーもなぜかあった。台形だ。っていうかコーヒーメーカーはあった。グラインダー機能はないが、水を入れておけばチョロチョロ抽出されるやつだ。つまり忘れるくらい昔にこれを使っていたのか、使うために譲り受けたが結局使わなかったのだ。まあそれはおいといて、YouTuberが楽しそうにやっているハンドドリップがやりたいのだ。決心がついた私は、コーヒーメーカーを含め今持っているコーヒー器具を全て捨てた。ゼロからスタートしたかったのだ。

近所のカフェ

近所に、いつも満席の人気カフェがある。ここではコーヒー器具も少し売っていて、ガラス製HARIO V60と、V60用HARIO公式ペーパー、HARIOのスケールを一気に買った。後日マグもここで買った。このカフェは自家焙煎のコーヒー豆も販売しており、どれも安くて美味しい。現在もよく通っている。

グラインダーでズルしてしまったが、基本的にネットで買わずに近所で器具も買うと気分がアガる気がする。

味は正直よくわからない

機材は揃ったので、YouTubeを観ながらコーヒーを淹れてみる。苦い。今思うと相当深煎りの豆だ。うまいかどうか、正直よくわからない。でもドリップバッグよりはいいのかも?ぐらい。どうすればもっとおいしくなるのか。どうすればお店並みになるのか。飲むたびになんとなく味が違う気がする。あれこれ試してみるうちに、次の豆にいってみる。なるほど豆によってこれまでの知見が通用しない。深煎りの淹れ方と浅煎りの淹れ方は違うらしい。もっといろんな味が知りたい。いろんなカフェに行ってみる。もっといろんなことが知りたい。コーヒーの本も読む。

自分の好みの味とは

コーヒーの本に「自分の好みの味を探そう」とある。自分の好みって何なんだろう。自分の好きな味って何なんだろう。

浅煎りケニアの味は分かりやすい。グレープフルーツみたいな味がする。オレンジみたいな味のコーヒーもある。深煎りも美味しいやつは甘い。ケーキに合う。中煎りも大人な感じでうまい。どうせならもっと味がわかるようになりたい。味の解像度を上げるためには、味の経験値が必要である。できるだけ沢山のコーヒーを飲んで、できればがんばって言語化して記録する。記録できれば比較や再現、追求ができる。こうして、どんどんとコーヒーにハマっていった。

コーヒーのおもしろさ

おいしい

一番大事なのがこれ。コーヒーはおいしい。何年も飲んでても気が付かなかった。コーヒーはフルーツ。味の幅が広く、ダークチョコレートみたいなコーヒーもあれば、イチゴみたいなコーヒーもあるし、桃みたいなコーヒーもある。無限に味を探求できるおもしろさがある。 淹れた後もコーヒーは温度によって味が刻一刻と変わる。

昔はアツアツが一番おいしいと思っていたが間違っていた。アツアツは、ただ味が分からないだけだったのだ。うまいコーヒーはちょっと冷めたぐらいが一番うまい。果実味がグッと増えてジュワッとした甘味が出てくる。十分冷めてもなおうまい。

楽しい

コーヒーは化学実験みたいな側面もあり、PDCAを回しやすい。粉にお湯をかけるだけと言う一見簡単な作業に見えて、お湯の温度、投入タイミング、投入時間、投入回数、投入量、豆の重さ、挽き目、透過式・浸漬式、様々な変数の掛け算で複雑に味が変わる。 前回とは1つだけ淹れ方を変えてみたり、ガラッと違うことをしてみたり、器具を変えてみたり、同じ器具でも練度をあげたり、いろいろな楽しみ方がある。 かつ料理ほどの複雑さはなく、シングルタスクでよいので集中できる。この適度さ具合も自分に合ってる感じがする。 適度に忙しいので、暇に思う感覚は消えていた。5秒暇なんじゃない。5秒で味が変わるのだ。

そんなに高くない

お店で飲んだら1杯700円のコーヒーも、豆を買って家で飲めば原価180円である。もちろんバリスタの技能や道具、お店の雰囲気はお金に変え難いのでお店にも通うのだが、1日多くても500円くらいの趣味である。

出会い

美味しいコーヒーを探すためなら、敷居が高そうなお店だって入っちゃう。外国人観光客ばかりのオシャレすぎるお店だって勇気を出して突撃しちゃう。何度もかよったお店で顔を覚えてもらってコーヒーの話をして「カップコメント上手ですね」なんて持ち上げてもらったりしちゃう。

以前の自分なら絶対にしなかったようなカフェ巡りをやっているし、幸いコーヒー屋さんが多い地域なのでいくらでも新しい発見、新しい出会いがある。

会社やコミュニティにもコーヒー好きは必ずいるので、コーヒートークで盛り上がれる。

現在

毎日2〜3杯ハンドドリップで淹れている。ハンドグラインダーは若干面倒だが、このゴリゴリ感も悪くない。コーヒーは五感で感じるものなのだ。でも電動グラインダーも気になる。

クレバードリッパーも買った。V60も使っている。豆によって合う合わないがあるような気がするので使い分けている感じだ。現状は、「夏はV60、冬はクレバー」という適当な印象だ。V60はスッキリしているので、夏にスッと飲みたい。クレバーはマッタリしている気がするので、冬にホッと飲みたい。

基本はV60で30秒毎に中湯するシンプルレシピ。最初の蒸らしは40ml、次は+60ml〜80ml、その次も+60ml〜80ml、5投目で2分になり、2分30〜3分以内に落ち切りで大体やってる。日によって2投にしたりスピンしたり蒸らしを1分にしたりと色々試したりしているけど、基本的にシンプルなレシピしか続かない。

豆は相変わらずいろんな店で買っている。いろんな味を探究しつつ、毎日飲むならエチオピアとかペルーのウォッシュドみたいなスッキリ丸い優しい味かなー。数年後の自分はどう思ってるかな。100g 2700円くらいの高い豆(シドラ)も少し買ってみたが、まだそこまで良さと言うか違いがわからない。

エスプレッソもたまに飲んでいる。おいしいけど自分には味が強烈すぎて、それぞれの違いがわからない。

マグはORIGAMIのバレルアロママグ。香りがカップに閉じ込められて感じやすいし、手触りもやさしくてあたたかい。

サーバーはKINTO。最初はサーバーを使わずにマグに直接ドリッパーを乗せていたが、サーバーを使うと圧倒的にそれっぽさがある。これほど機能性ではなく満足性がグッと上がる道具はないだろう。やはりコーヒーは五感で味わうものなんだ。

コーヒーは飽きないのでいくらでも楽しめる。コーヒーはいいぞ。

型システムのしくみ / 遠藤侑介

型システムのしくみが発売されました。

本書の概要は著者の記事をご覧ください。

実は本書のレビューに参加していました。

今回は、発売前の本のレビューに参加したこと自体が初めての経験だったので、日記を書いておこうと思います。

型システムのしくみを手に持っている
献本もいただきました。

きっかけ

私は隔週でRBS関連のミーティングに参加しています。そこで著者の@mameさんがレビュアーを募集していたので手を挙げてみました。

私は情報系の学科を出てはいますが、かなり学業にやる気がない遊んでばかりいた学生でした。そのため型システムに関する知識はもちろんCS自体学んできたとは言い難い人です。新卒の職場も"工場の社内SE"という肩書きで、工場でネジを持って走ったりメールの内容を読んでデータ入力したりしていました。その後プログラミングの楽しさに気づき、紆余曲折を経て現在はWeb系のアプリケーションプログラマーとして働いています。

そんな私に型システムの本のレビューができるのかは大変不安ではありました。しかし、著者のmameさんからそんな人にこそこの本を届けたいと後押ししてくださったので、ものは経験だと自分に言い聞かせてレビューに挑戦してみることにしました。

レビュー

やるからにはバリュー出すぞと意気込んではみましたが、いざ本書を読んでみると内容が素晴らしく学びになるので、これではただただ先行して本を読ませていただいているだけじゃないかと焦りました。今の私にできることはなんだろうと考えました。専門的な指摘はどうせできないし他のレビュアーの方にお任せすることにして、私はいっそ、「これはシステム開発とかでよくある、新機能をユーザーに届ける前の検証としてのユーザーインタビューなんだ。そう思いこもう。」と割り切って自分なりに思ったことや分からなかったところをメモしていくことにしました。それしかできない。

本書では型システムの分からない読者にも伝わるように丁寧な説明が随所に盛り込まれています。おかげで私にもスラスラと読み進めていくことができました。

書いている本人としては分かりきっていることを、分からない人に対して分かるように伝える。これがいかに難しいことなのか、子育てをしていると痛いほど感じます。 にも関わらず、読者が「なんで?」と思ったらすぐ補足説明されていたり、こういう言い回しをしているのはこういう理由だよと説明されていたりと、読者に寄り添った説明が随所に盛り込まれていて非常に読み進めやすかったです。

「型システム入門」の入門

私は「型システム入門」(TAPLの日本語訳本)をこれまでに2度読もうと挑戦したことがあります。しかしながら、理解度は1%ぐらいで分からないまま体に浴びせかけただけです。 mameさんにも昔「型システム入門の入門が欲しいです」と言ったことがあります。本書がまさにそれなんです。

本書を読んだことで、3度目の挑戦をやってみようと思います。

貴重な経験と、貴重な本をありがとうございました。

こちらでご購入いただけます

型システムのしくみ ― TypeScriptで実装しながら学ぶ型とプログラミング言語www.lambdanote.com

SteepにGitHub Actionsフォーマットを導入した

https://github.com/soutaro/steep/pull/1516

まあそれだけなんだけど

rubocopには--format githubというオプションがある。

このオプションをつけると、指摘点が1行のシンプルなフォーマットのテキストで表示されると思う。

このフォーマットはGitHub Actions上で効果を発揮するものになっていて、PRのコード上にまるでbotがコメントを挿入したかのようなテキストを表示させることができる。(厳密にはコメントではない)

rubocopの--format githubオプションについては、こちらのブログでも紹介されている。

https://r7kamura.com/articles/2022-06-21-rubocop-format-github

この機能は私も好きで、実際にrbsリポジトリーでもgem_rbs_collectionリポジトリーでも導入している。

で?

このオプションをSteepにも導入してみた。

狙っている使い方は、型検査をよりやんわり伝えると言う方法だ。

現状型検査に一つでも違反していたらCIを落とすというやり方はかなりストイックなもので、ライブラリーレベルならやれるが、アプリケーションレベルでやるのはかなり大変か限定的な使い方になるだろうと思われる。

しかし、もう少し広い範囲で、やんわりとした使い方をしたい。つまり型検査に違反していてもCI上は落ちず、かつ無視はできないレベルで視界には入るので注意喚起ぐらいにはなる。という使い方だ。

もしこの使い方ができれば、少しずつのCI上での型導入が進めやすくなるのではないかなと狙っている。

使われたらいいなあ。

paranoia gem用の型を生成する

一言で言うと

orthoses-paranoiaを作った。

背景

以下のようなARコードがあったとして、with_deletedの型が分からないので後のコードが全てuntypedになってしまう問題があった。

User.with_deleted.comments.where(...)
#  ↑ここはUser型
#               ↑ここはUser::ActiveRecord_Relation型であってほしい
#                しかし現状メソッドの定義がなくuntyped

このwith_deletedparanoiaで生えてくるメソッドで、中身は簡略化すると極単純なものだ。

def with_deleted
  unscope where: paranoia_column
end

返り値はUser::ActiveRecord_RelationだったりComment::ActiveRecord_Relationだったりと、使用するモデルによって変わる。

RBSはこういうRailsによくあるパターンが不得意だと思っていて、解決策としてorthosesを設計したのだった。

orthosesの拡張として実装することで「acts_as_paranoidが呼ばれたモデルに対して所定のメソッドを追加する」というコードを素直に書いて、paranoiaに対応できた。 orthoses-railsにがっつり依存しているが、それだけに考えることが少なくて済む。orthoses便利。

2024年のrbsアップデートまとめ

ruby 3.4リリース

Ruby 3.4が出て2月ほどが経ちましたね。 私は去年のRuby 3.3リリースに合わせたRBSの記事を書かせていただきました。

新機能ラッシュ! RBS最新情報をキャッチアップ | gihyo.jp

今年はこの企画自体がなかったので個人のブログで簡単に書いておくことにします。

RBS::UnitTestの追加

RBS記述が実際のRuby許動に沿ったものなのかテストするためのフレームワークが提供されるようになりました。 これまでrbsリポジトリー内でだけ使われていたものを、gemなどでも利用できるようになっています。

  def test_foo
    assert_send_type(
      "(Integer) -> String",
      YourGemClass.new, :foo, 123
    )
  end

サンプルコードを読み解くと、YourGemClassというclassのインスタンスメソッドfoo123という引数を与えて動かすと、(Integer) -> String、つまり引数にはInteger、返り値にはStringが返ることを期待するテストコードになります。 さらに裏では記述されたRBSもチェックしています。RBSの記述、実装者の期待値、実際のRubyの挙動の3点から整合性を確認できます。

Recordタイプにオプショナルなキーを指定できるように

Recordタイプ(e.g. { foo: Integer }。固定のHash的なやつ)で、あってもなくてもどっちでもいいけどもしもあったら型チェックしてほしい。。。そんな指定ができるようになりました。

# aaa({ foo: 123 }) => OK
# aaa({ foo: 123, bar: 'zzz' }) => OK
# aaa({ foo: 123, bar: 234 }) => Error
# aaa({ foo: 123, zzz: 'zzz' }) => Error
def aaa: ({
  foo: Integer, # これまで通りの必須キー
  ?bar: String # 必須ではないオプショナルキー
}) -> void

引数が全くわからない場合のメソッド記述が可能に

メソッドに対して、引数が全くないのか、なんでも受け付けるのか、まったく分からないことを表すことができるようになりました。移譲などで引数の数や形式が変わる場合に便利です。

# 従来のあらゆる引数を受け入れる記述
# これでは引数がまったくないことを表せない
def foo: (*untyped, **untyped) -> void

# `(?)`と書くと「引数まったくわからんから何でもおk」を表現できる
def foo: (?) -> String

# Procやブロックでも`(?)`が使える
def bar: (^(?) -> void) { (?) -> void } -> void

RBSトークン列が取得できるように

RBS::Parser.lex(source)

RBSライブラリーのRuby APIとして抽象構文木(AST)を構築する前のトークン列を取得できるようになりました。 このAPIrubocop-on-rbsを開発していて欲しくなった機能で、抽象構文木では取りにくいトークンの位置情報を取得して問題を報告させるのに役立てています。APIはPrismを参考にしました。

def foo:  (aaa: Integer) -> void
         ^ スペースが2つ続いていることを指摘したい

Genericsの強化

型引数の制約条件が緩くなりました。あんまり使うことない?

interface _Foo[T < String?]
  def foo: () -> T
end

type foo = _Foo[String]     # OK
type bar = _Foo[Integer?]   # Error

また、型引数にデフォルトの型を指定できるようになりました。型引数は一度つけば毎回指定しなければいけなくなるのが面倒ですが、これで使いやすくなりそうです。

# `_Each[Integer]`だけで使えるようになる
interface _Each[Element, Return = void]
  def each: { (Element) -> void } -> Return
end

bundled gemが徐々にrbsリポジトリーを卒業していくことに

bundled gemはRuby本体でのメンテナンスを離れてgemとして独立していったgem達です。つまり普通のgemにかなり近い存在です。これをrbsリポジトリーで持つのはもう違うよねーということで、rbsリポジトリーからbundled gemをなくしていくことになっています。ユーザー的にはあんまり意識することはないかもしれません。PR送る先で困るかも。。。わかりやすく整理します。

もしmanifest.yamlを書いている上級者の方々は、ここにbundled gemを書かないようにしていってくれると助かります。

Kernel#NamespaceKernel#TypeName がdeprecatedに

RBSに関心がなくても遭遇する可能性があります。 Namespace()というメソッドとTypeName()というメソッドがあるのですが、今後RubyでNamespace機能が搭載される予定ですしややこしいということでdeprecatedになり、使用しているコードで警告が出るようになりました。 もし警告を見たらライブラリーをアップデートするか、警告を出している(TypeName()を使用している)ライブラリーにPRを送ってみてください。

今後

2024年の出来事ということでここまでで終わりですが、RubyKaigi 2024でも話されたrbs-inlineに対する界隈の関心は高く、rbsリポジトリーにいつかmergeされる予定とどこかで聞いたことがあるので注目ですね。