Ruby のような実行時に何でもできる言語だと、ユーザが書いた Ruby のコードを実行したり、プラグインの gem をインストールするだけで使えるようになったりする仕組みを提供できる。
例えば spring は ~/.spring.rb や config/spring.rb を自動的に読み込み、また spring-commands- で始まる gem を自動的に require し、プラグイン側は Spring.register_command
で独自のコマンドを定義できるようになっている。
- https://github.com/rails/spring/blob/v1.1.2/lib/spring/commands.rb
- https://github.com/jonleighton/spring-commands-rspec/blob/v1.0.1/lib/spring/commands/rspec.rb
pry も似たようなかんじで、~/.pryrc や ./pryrc を読み込んだり、pry- で始まる gem を自動的に require して、プラグイン側は Pry.commands.import
等で独自のコマンドを定義できるようになっている。
- https://github.com/pry/pry/blob/v0.9.12.6/lib/pry/pry_class.rb#L109
- https://github.com/pry/pry/blob/v0.9.12.6/lib/pry/plugins.rb#L70
- https://github.com/deivid-rodriguez/pry-byebug/blob/v1.3.2/lib/pry-byebug/commands.rb#L260
一方、Haskell のような言語だとこういうことをするのは難しい。 Haskell で書かれていて Haskell で拡張可能なプログラムというと XMonad くらいしか思い付かないんだけど、XMonad は「拡張可能なウィンドウマネージャ」というより「ウィンドウマネージャを簡単に作れるライブラリ」に近くて、実際 XMonad の設定は main 関数を持つ普通のプログラムとして書かれる。
XMonad の場合、設定をコンパイルして得たバイナリは ~/.xmonad/xmonad-$arch-$os
に作られるんだけど、xmonad コマンドは ~/.xmonad/xmonad-$arch-$os
を exec し、それに失敗したらデフォルトの設定で xmonad をスタートするラッパーの役割しか持っていない。
Haskell で拡張可能なプログラムとして yi を忘れてた。 yi も XMonad と同じ方式で、設定ファイルは main 関数を持つ普通のプログラムとして書かれるっぽい。XMonad 方式の設定を実現するための dyre というライブラリがあり、yi はこれを使っている。
Haskell で拡張可能なプログラムを書くとしたら、XMonad のようなアーキテクチャにするのがいいのかもしれない。 pandoc は内部では新しい Reader / Writer を追加できるようなアーキテクチャになってるけど、ユーザ側が好きな Reader / Writer を追加できるようなものではなさそう。