エディタさん落ち着いて

プログラミングは楽しいべきだろうか。

私は少なくともそうあるべきだと思う。 プログラミングは楽しいからオープンソースが存在するのだし、こうして趣味の時間として割くことができる。 例えばRubyというプログラミング言語は楽しさを標榜している珍しい言語である。 便利さとか、快適さが楽しさに直結すべきだから、別にC++であろうがJavaであろうが楽しくなる要素は大いにあると思うし、楽しくないRubyも存在する。

楽しくないRubyとは。

楽しくないRubyはコード量の多さだろうか、あるいはどういった意図で存在しているかもわからない暗号のようなコードだろうか。 確かにそれらは楽しさには直結しにくいけれども、そういったコードはリファクタリングやパターンで楽しいコードになると思う。 Rubyが楽しいのは他の言語に比べてそういった作業が簡単に思えるからだ。

余談だが型が楽しいのか楽しくないのかは人による。 私は最初TypeScriptの物珍しさから型を楽しんでいたが、最近はもっぱらRubyやJavaScriptに落ち着いている。 嫌いになったわけではないし、型そのものは便利だとは思うがこの後に述べる事と共通している気がする。

例えばこんなコード:

desc "HTMLファイルから記事を抽出する"
task extract_entries: :environment do
  entry_list = []
  Dir.glob("www/20231030/*.html") do |html_path|
    File.open(html_path, "r") do |html|
      doc = Nokogiri::HTML5.parse(html)
      entry_list << doc.css(".entry").map do |entry|
        {
          href: entry.at_css("a[href]").attr("href")
          pub_date: entry.at_css("date[pub_date]").attr("pub_date")
        }
      end
    end
  end
  entry_list
    .flatten
    .sort_by { |entry| entry[:pub_date].in_time_zone }
    .each { |entry| Entry.create(entry) }
end

これは割と私の中でパターン化しつつあるコードなのだが、一目でこのコードを理解したいだろうか。 これでも比較的簡略化してはいるのだけれども。 ループでハッシュテーブルに変換して最後に並び替えてDBに突っ込んでいるだけだ。 熟練のプログラマならこれくらいのコードは大した量に感じないだろう。

ただ少なくとも私はそう思わない。 他の言語であろうとも、だいたいこのような書き方に近い表現はできると思う。 多かれ少なかれこんなコードの書き方は特に珍しくもないと思う。

最近の私ならこういう感じにリファクタリングする:

class EntryList
  def initialize
    @entries = []
  end

  def <<(entries)
    @entries << entries
  end

  def save
    @entries
      .flatten
      .sort_by { |entry| entry.pub_date }
      .each { |entry| Entry.create(entry.to_h) }
  end
end

class EntryParser
  def self.entries(html)
    doc = Nokogiri::HTML5.parse(html)
    doc.css(".entry").map do |entry|
      new(entry)
    end
  end

  def initialize(entry)
    @entry = entry
  end

  def to_h
    { href: href, pub_date: pub_date }
  end

  def pub_date
    @entry.at_css("date[pub_date]").attr("pub_date").in_time_zone
  end

  def href
    @entry.at_css("a[href]").attr("href")
  end
end

desc "HTMLファイルから記事を抽出する"
task extract_entries: :environment do
  entry_list = EntryList.new
  Dir.glob("www/20231030/*.html") do |html_path|
    File.open(html_path, "r") do |html|
      entry_list << EntryParser.entries(html)
    end
  end
  entry_list.save
end

Rubyはすべてがオブジェクトなのでこういう感じで自由にクラスを作ることができる。 書いている行自体は最初よりも多くなっているが、それぞれの役割がはっきりしていると思う。 最初は面倒に思うかもしれないが、案外なれてしまうとこれくらいならササッと作ることができる。

これくらいの量ならsaveメソッドにTransactionで書き換えようとかそういう工夫も思いつきやすい。

ではなぜ私がこのコードを作り出すことができたのか。

それはエディタの力を借りたから? 確かに空白行を除去したり、インデント幅を揃えるためにフォーマッターは利用している。 でもそれはRuboCopではない。 副次的にRuboCopが煩く書く行が多いとか指摘しつづけた結果でもあるのだが、このコードベースを生み出したのは間違いなく私自身である。

私はデフォルトのフォーマッターにRuboCopを使うことを諦めた。 なにか新しい変更が入るたびにコードに細かい指摘を入れてくる。 最近驚かされたのはJavaScriptのような省略記法だ。

>> foo = 'bar'
=> "bar"
>> baz = { foo: }
=> {:foo=>"bar"}

JavaScriptの{ foo }はすんなり受け入れられるのだが、{ foo: }はタイポにしか思えない。 他にも空のdefは一行で書けとか重箱の隅をつつくような指摘が多すぎる。 最近はもっぱらCIですら実行しなくなった。 ただあくまで任意のタイミングで実行はしているからRuboCopそのものは捨てていない。

その点Rufoというフォーマッターを使うようになってから私のRubyに対する楽しさはより楽しさを増した気がする。 単に失いかけていた楽しさが戻っただけなのかもしれないが、それは重要だ。

エディタはVSCodeを使っているのだが、Ctrl+Sをしたときに自動でフォーマットするようにしている。 モダンなフォーマッターはOpinionatedという単語をよく使う。 あまり気にしていなかったが、RufoもOpinionatedなフォーマッターではあるが、やれクラスにドキュメントを書けだとか、ありとあらゆるすべてのファイルに対してfrozen_string_literal: trueを書けだとか必要以上のことは要求してこない。

最近のプログラミング言語が徐々に陥りがちなことはエディターがプログラマーに対して物申す(Opinionated)ようになったことだ。 やれハンカチは持ったか、ネクタイは曲がっていないか。 そういったことはエディターに求めていないのだが。 まるでプログラマーはこう振る舞うべきだと言うようになった。 あなたはエディターだ。 ただ私にプログラミングだけをさせてください。

例えばGoという言語はいつか学びたいとは思っているのだが、言語レベルでこうすべきだというフォーマッターが存在しているのは困る。 静的型付け言語だからフォーマットとコンパイルが紐付いているのは理にかなってはいると思うのだけれども、正直タブが必須だったり、使っていない変数や関数をとりあえず放置しておくことはできない。 おそらく私の食指が動かないのはきっとこういう理由も大きい。

エディタというのは便利であるべきだ。 私は物静かなエディタが好ましいと思っているが、それはさながら秘書や執事のようなものだ。 私が書き散らかした空白行を黙って片付けてくれたり、使っていない変数をこっそり指摘してくれるものであるべきだ。 衆目の前で大声で喚き散らして主人に恥をかかせるものであるべきではない。

いくら物静かなエディタがよいとは言っても、OS標準のテキスト帳を使うなんて非効率すぎる。 ではもっと賢い人たちが使うエディタはどうだろうか。

本家のVimやNeoVimなどのエディタも突き詰めていけばVSCodeとあまり変わらない気がする。 それどころかVimはVimでひとつの産業とも思えるくらい習得までの道のりが長い。 OSをセットアップして、プログラミングを始めたいと思ってダウンロードして、プログラミングを始めるまでにダウンロードするパッケージが延々と存在するだろうし、定期的にメンテナンスもしなければならない。 私はviはどこでも使えるので好きだけれども、vimは苦手である。

そう考えると本質的にエディタはどれも一緒であるといえる。 プログラミング言語のように、大抵のエディタはインターネット経由で機能を拡張できるからだ。 あるエディタで取り入れられた優れた機能は別のエディタに拡張機能として受け継がれる。 VSCodeにVimの拡張機能があるのはおそらく誰でも知っているだろう。 Vimを使いたいならVimを使えばいいのにと私は思うのだが。

何なら私は最初の数年間はずっとSublime Textのキーバインドを使っていた。 主要なショートカットキーは同じだったので自然と離れられたけれども。 だからVimもEmacsも興味のない人間からしたら一緒だし、それはSublime TextやWebStormなどでも一緒である。 全くエディタの動向を気にしていないわけではないけれども

RuboCopもオプトイン方式なので嫌だったら使わなければよいし、面倒ではあるが必要なルールだけを選んで使うことはできる。 エディタもそうあるべきだとは思っている。 VSCodeはまだそのあたりの塩梅が辛うじて機能している。 だけども私のsettings.json(ユーザーの設定機能)はすでに60行近くあるし、度々なにかの変更があるたびに増えている。 これでも取捨選択はしている方なのだが、それだけインストールしたあとにするべき変更が多いことを意味している。

設定ファイルを同期しやすかったり、テキストファイルで管理できるのはいいことだと思うのだけれども、やはりすべてはオプトインにすべきだった。 scm.diffDecorationsGutterPatternという設定があるのだが、これがサポートされるまでは本気で乗り換えを検討していた。 そうならなくてよかったけれども、たった一つの身勝手な変更が優れたエディタを一発で台無しにしてしまうリスクをどうか理解してほしい。

VSCodeそのものはバージョンアップの度にまた何か謎の機能が追加されないか恐怖を覚えるのだけれども、幸いオプトアウトする方法があってもGitHubをこれまでいろいろと改悪したMicrosoftのことだ。 いずれ使い物にならないくらいの大改変が将来的に発生するのは避けられない。 Microsoftにとってはここまで大事に大事に育ててきた果実をいつか収穫する時が来るはずだ。 これは冗談じゃない。

それくらい本当にみんなこのエディタを使っている。 少なくとも私がZoomなどの画面共有や、あるいはかつてオフィスにいた頃も一部の例外を除いて全員がこのエディタを入れていた。

だからDoomsdayはいつか来る。

でもそれが今日でないのであれば使わなければならないのだ。 東京に住んでいる人たちが首都直下地震が必ず来ると脅されていても、結局そこに住み続けるのと変わらない。 私にも彼らにもそれぞれ暮らしたい人生がある。

便利さと多機能であることはエディタを快適なものにするために必須なものなのだが、どうもなにか履き違えていないだろうか。

例えばVSCodeでRubyの拡張機能といえば長らくRubyというものがあった。 これを入れると入れないでは使い勝手が天と地ほど変わる。 長らくお世話になってきたのだが、残念ながら今GitHubのリンクを開くとThis repository has been archived by the owner on Jul 31, 2023.というメッセージが表示されている。 エディタが代替案を紹介してくれるのはありがたいのだが、問題はそれがRuby LSPという拡張機能である。

この後継機能を入れてみてまずRubyをインストールしろと求めるようになった。 先代はそのようなことを口にはしなかった。 エディタは同時にファイルブラウザとか、ファイルリーダーの役割もあると思うのだけれども、どうもファイルの解析に必要らしい。 これは私がFormat on Saveを有効にしているのもあるからだと思うが、問題はそれだけじゃない。

ruby-lsp

Required Ruby Version: >= 3.0

なんとこの拡張機能がサポートしているRubyは3系からなのだ。

さすがに1.8系とか1.9系のコードはもうどこにもないけれど、2系のコードなんてまだ大量に存在している。 確かにRuby 2系はもうEnd of Lifeを迎えているからこれ以上2系の面倒まで見ろとも言わないのだけれども、これがエディタに事実上必須の拡張機能であることから2系のプロジェクトを開いてファイルを保存するたびにエラーメッセージが表示されることを意味している。

確かにこれくらい煩くしないとバージョンアップの移行はしないのかもしれないが、ちょっとこれは魔女裁判のようなものではないだろうか。 それならVSCodeそのものを2系統にわけて使えばよいのだろうか? あるいは同時にインストールしてそのたびにsettings.jsonを生成しなければならないのだろうか。 誰がそんなことをしたいのだろうか。

これはあくまで邪推なのだが、Shopifyくらいの大きな企業にもなると大量の従業員がいる。 Rubyという言語を好んで使っているプログラマーは必ずしもオブジェクト指向の造詣に深いとも限らない。 誰しも先程例示したコードは書きうるし、私もコードベースが整っていないプロジェクトであれば積極的にリファクタリングしようなんて思わない。 そうなるとやっぱりコードの品質が担保できない問題が出てくるので、たとえ昨日プログラミングを覚えたばかりの人でも熟練したプログラマーのようなコードに寄せるようにしたいのだろう。

でも結局はプログラミングの本質なんてただの手続き言語かもしれないが、そこには先人たちが編み出した手法を使ってできる限りそう感じさせないように作り変えた。 それはデザインパターンと名付けられている。 だからコードレビューは重要だし、このプロセスはまだChatGPTですら完璧にできる分野ではない。 インターネットに繋がっていようが、ただのエディターができることなんてたかが知れている。

将来的にはよくプログラマーの仕事はすべてAIに取って代わるなんて言われているが私はそうは思わない。 書籍から始まり、検索エンジンやStackOverflowが出てきて、つい最近ChatGPTが加わっただけだ。 プログラマーは環境構築やコンパイルのような地味な作業でもこなしてしまうのだが、ビジネスサイドの人々はそういった作業を一切好まないのでSiriのような音声アシスタントに自分のしたい事業を伝えて、その要求にすべてこたえるくらいの完璧なWebサイトが仮に出力できたとしてもサポートとしてのプログラマーという仕事は残ると思う。 むしろ減るどころか増える可能性だってある。 プログラミングに関わらない人々が思い描くような終末論は当分訪れない。

ただしその可能性が永遠にないとは言い切れない。 ChatGPTが出る以前のディープラーニングとか、マシンラーニングの頃は0だと思っていたので私も考え方は変わった。 それくらいすごいイノベーションではあるけれども、まだまだ本当に呆れるくらい先だろう。 それだったら車が空を飛んでる方が早く実現するんじゃないかなと思っている。 人が乗れるドローンかもしれないけれども。

また話が逸れてしまったが、結論は今のエディタがだんだんAmazonやYouTubeのおすすめのように、見たくもないのにやるべきことをどんどん押し付けてくるので、これほど苦痛なものは存在しない。 人間の便利さとか、効率性に対する探究心は素晴らしいのだけれども、コードを書くのがbotや従業員だけとも限らない。 だからエディタとはいい関係を築いてきたい。

仮にVSCodeが死んでまた別の画期的なエディタが生まれたとしても、最終的に何かが陳腐化していくサイクルは誰にも止められない。 止められないかもしれないけれども私は言いたい。

私は頑張りやなエディタさんにどうか落ち着いてと言いたいのである。