GoユーザーがHaskell/OCamlのライブラリ配布で面食らった話

前書き

本記事画像には、Gopherを用いています。オリジナルのThe Go gopher(Gopherくん)は、Renée Frenchによってデザインされ、CC BY 4.0ライセンスが適用されています。

 

本記事は、「プログラミング言語間でのライブラリ配布の違い」および「GoユーザーがHaskellとOCamlの流儀に戸惑ってしまった話」を紹介します。「この記事を見ればライブラリ配布の手順が全て分かる!」という記事ではないので、ライブラリ配布方法を知りたい方は別記事を参照してください。

 

後書きで、関数型言語に対して感じていることも書いてます。

      

前提

私は、Go でしか OSS(ライブラリ)の配布経験がありません。ここでの配布とは、GitHub Repositoryをパブリックで公開しているという意味ではなく、プログラミング言語のエコシステムに従ってOSSを配布するという意味です。ライブラリインポートしやすい形で公開している、ぐらいのニュアンスです。

OCamlはまだ配布できていない

    

C言語、Java、Perl、Python、Ruby、ShellScriptなどの言語を使った経験もありますが、それらを使ったライブラリをOSSで配布したことはありません。つまり、C言語〜ShellScriptの配布方法を把握していません(C言語に関してはGNUでの管理方法を知っていますが、GNU以外の配布方法を知りません)

    

本記事では、HaskellとOCamlのライブラリ配布方法を「ちょっと面倒くさいな」ぐらいのニュアンスで扱いますが、もしかするとC言語やJavaなどのライブラリ配布方法と大差ないかも知れません。歴史のある言語ほど、エコシステム周りが弱めなのは仕方がないことです。後発言語は、歴史を踏まえて改良してくるので、後発言語の方が使い勝手が良いのは当然です。

   

Goでのライブラリ配布

Goでは、GitHubやGitLabなどにパブリックリポジトリを公開し、go.modファイルをプロジェクトルートディレクトリに配置すれば、ライブラリが利用できる状態になります。

go.mod ファイルは、以下のコマンドで作成できます。

余談ですが、この辺りの流れはGoユーザーだと暗記していると思っていました。しかし、プロジェクトを最初から作った経験がない人はパッと思い出せないようです。ペアプロしている時に手が止まっている人を見て、「あっ、覚えてないんだ」と感じた記憶があります。

         

ライブラリ利用者側は、”import github.com/username/mylib”とソースコード中に書き、go mod tidyコマンドでライブラリをダウンロードすれば、ライブラリを利用できます。

        

Haskellでのライブラリ配布 

GitHub等にライブラリを公開する点は、Goと同様です。Haskellの場合は、Hackageにライブラリを登録します。Hackageを利用するには、アカウント登録ページからアカウントを作成する必要があります。

                     

さらに、trustees(≒ 権限のある人)から承認を得ないと、ライブラリを公開できません。承認を得る方法は、メールです。この時点で「2025年で、メール……だと……?」と思いましたが、メールでパッチを送りあう運用で開発しているライブラリもあるので、そういうものだと受け入れました。

After your account is created, you cannot upload until you are added to the uploaders group. You can be added to the uploaders group by asking two confirmed uploaders to endorse you (information will be provided in the email).

Alternately, you can contact the hackage trustees at hackage-trustees@haskell.org and send an email (including your login username and the package you intend to upload) requesting to be added to the uploader group. (This measure is unfortunately necessary to prevent spam accounts).

[訳]

アカウントを作成したあとでも、uploaders グループに追加されるまではアップロードできません。uploaders グループへ追加してもらうには、すでに承認された uploaders 2名から推薦を受ける必要があります(メールで案内があります)。

あるいは、hackage-trustees@haskell.org 宛に、自分のログインユーザー名とアップロード予定のパッケージ名を記載したメールを送って、uploaders グループへの追加を依頼することもできます。(この措置は残念ながらスパムアカウントを防ぐために必要なのです。)

     

以下、参考までに私のつたない英語メールです。ちなみに、返事は15分できてビビりました。

承認を得たら、 公開対象ライブラリの tarball(今、tarballって通じます?)を stack sdistコマンドで作成します。Haskell は一度アップロードしたファイルを削除できないので、慎重に作業する必要があります。利用者のことを考えると、公開したライブラリを気軽に削除してはいけないのは Go も同じですが、実態としては利用者がいなければ Go ライブラリを削除してます。へへ。

 

気軽にアップロードしてミスすると、Haskell ではリカバリーしづらいです。ミスを防止するために、Package Candidates という仕組みが用意されていて、パッケージアップロードをテストする仕組みがあります。設定ミスを検知してくれたり、リリース後のサイトの見た目をチェックできます。

 

私は雑な性格をしているので、Go ライブラリでリリースをミスったら、気軽にパッチバージョンを上げてます。パッチバージョンは、オペミスも吸収してくれる便利な存在。しかし、Haskell では慎重さが求められます。性格の不一致感があります。

 

なお、私が Haskell で公開しようとしたライブラリは、nao1215/clockwork-base32です(リリース直前で面倒くさくなって、リリースしませんでした)。base32 の変種であり、公式ドキュメントはこちらにあります。作った理由は、Haskell 初心者の私でも実装できる規模感の仕様であること、前職のテックリードが Go 版の実装をしてて印象に残っていたことが挙げられます。

     

OCamlでのライブラリ配布の場合

GitHub等でライブラリを公開する点は、GoとHaskellと同様です。OCamlの場合は、opam(OCaml Package Manager)としてライブラリを公開します。

 

ライブラリを公開する場合は、ライブラリの基本情報を <pkg>.opam に書き出します。雛形を出力するコマンドはありますが、手書きで埋めなければいけない部分がそれなりにあります。

 

以下、私が公開用に作った opam ファイルの例です。 どのようなライブラリか、どこで開発しているか、メンテナは誰か、バグレポはどこに出すか等の情報を書く必要があります。このファイルに問題があるかどうかは、opam lintでチェックできます。

ビルドやテストが通り、依存関係の情報を正しく記載できた後は、公開するだけです。OCamlの場合は、ocaml/opam-repository に Pull Request を作る必要があります。私は、他人のリポジトリにPRを好んで作るタイプではないので、ここで少し抵抗感がありました。

 

なお、PRは自動で作れます。opam publishコマンドを利用すると、自動でPRが作られます。ただし、GitHub Personal Access Token を要求されます。PRが作成されると、80個以上のジョブを持つGitHub Actionsで検査され、その検査の後で管理者から approve を得るとリリースされます。

 

以下は、私がリリースを試みている、nao1215/stringx ライブラリです。大したライブラリではないので、レビューで弾かれないかなとビクビクしてます。

   

個人的には、Haskell よりも OCaml の方が配布が楽かなと感じました。その一方で、Goと比較して気になりポイントが何点かありました。

  • ライブラリ名が被ってはいけない(名前空間が貧弱)
  • リリース間隔が長くなる(PRマージまでのリードタイムが長い)
  • 運営方法が続くか心配(PRレビューするコントリビュータを維持できるか)

少し気になるぐらいで、私のようなホビーユースであれば、特に困る話ではありません。

    

最後に:関数型言語に感じていること

一年ほど、思い出すように関数型言語にトライしていますが、まだお友達になれた感じがありません。

 

最初は、作りたいソフトを関数型言語で書く理由がないことに困っていました。最近は解像度が上がって、「関数型言語のエコシステム、もう少し現代的にならないのかな」と感じています。

今回、話題に出したHaskellは1990年生まれ、OCamlは1996年生まれ。2010年以降に登場したプログラミング言語と比較して、エコシステムが弱めになってしまうのは仕方がない感があります。

    

「2010年以降に登場した関数型言語を使えば、エコシステムが強固なのでは?」と発想を転換すると、Elixir、Elm、PureScript、ReasonML、Gleam 辺りが候補に上がります。

  • Elixir/Gleam:BEAM VM 上で動く分散/並行処理向け
  • Elm/PureScript/ReasonML:JavaScript へのコンパイル前提

私は、どちらかと言えばターミナルで普段使いするコマンドを作りたいので、そのような用途では上記の言語はやや魅力的に映りません。エコシステム以前のところで、「なんか違う」という状況です。

 

関数型言語全体に関して、現状の個人的な結論としては以下です。

  • システムプログラミングで関数型言語のエッセンスを感じたい場合、Rust
  • シングルバイナリを作れる関数型言語を使いたい場合、OCaml
  • サーバー向けの場合、Elixir/F#/Scala
  • フロント向けの場合、Elm
  • エコシステムが現代的で関数型言語エッセンスを持つ言語の場合、Rust/Kotlin

エコシステムや標準ライブラリが強く、システムプログラミング向けの関数型言語を作れれば、一定の支持を得られるのではないかと妄想しています。しかし、そのポジションに最も近いのは Rust なんですよね。ただ、私がプログラミング言語を作る趣味を始めるのであれば、OCamlでコンパイラを書いて、Rust のような関数型言語を作る気がします。

    

   

おすすめ