オフトゥン大好き。

惰眠系プログラマの作業ログで( ˘ω˘ ) スヤァ…

Elixir で漢字を判定する方法

Elixir で漢字を判定する方法。Unicode における漢字のコードポイントは以下の記事にまとまっている。

tama-san.com

これをもとに判定モジュールを書いてみた。

Elixir 漢字判定モジュール

文字列を charlist に変換して Enum.filter で漢字だけ抜き出してみると以下のようになる。

❯ iex -S mix
Erlang/OTP 24 [erts-12.3.2.2] [source] [64-bit] [smp:16:16] [ds:16:16:10] [async-threads:1] [jit]

Interactive Elixir (1.13.4) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> text = """
...(1)> 吾輩は猫である。名前はまだ無い。
...(1)>  どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。
...(1)> """
"吾輩は猫である。名前はまだ無い。\n どこで生れたかとんと見当がつかぬ。何でも薄暗いじめじめした所でニャーニャー泣いていた事だけは記憶している。\n"

iex(2)> text |> String.to_charlist() |> Enum.filter(&Kanji.kanji?/1)
[21566, 36649, 29483, 21517, 21069, 28961, 29983, 35211, 24403, 20309, 34180,
 26263, 25152, 27875, 20107, 35352, 25014]

iex(3)> text |> String.to_charlist() |> Enum.filter(&Kanji.kanji?/1) |> String.Chars.to_string()
"吾輩猫名前無生見当何薄暗所泣事記憶"

ちゃんと判定できていそう。

Emacs: git-gutterのhunk機能を使う。そしてhydraでちょこっと便利にする。

Emacsを愛するみなさん、こんにちは。
全然ブログが更新できてなくて「うわー!はてなのパスワードわすれたー!」とか思ったけど実は1Passwordで管理していたからなんとかなったid:nukosukeです。

今回はEmacsでGitのhunkを操作(または走査)するためのちょこっと便利な設定について書きます。

EmacsでGitというと最初に思い浮かぶのは、おなじみmagitですよね。僕もこれまでGit操作のほぼ全てをmagitに頼ってきました。
いやホントに、Gitバージョン管理が当たり前になったこのご時世にもしmagitがなかったらEmacsを使い続けることに挫折していたかもしれません。膨大なGit機能にフル対応しているのはおそらくこのパッケージだけでしょう。

ところが、最近になって差分箇所の視覚化にだけ使用していたgit-gutterにもhunkを操作する機能があることを知りました。この機能についてはgit-gutterのREADMEを見ればちゃんと書いてあるのですが、手早くいい感じの差分マーカーを付けたくて二次情報しか見てなかったこの愚か者は全然知りませんでした。反省しています。

github.com

ドキュメントにあるようにgit-gutterは以下のような機能を備えています(今回使用しないものは割愛)。

コマンド 機能説明
git-gutter:previous-hunk 1つ前のhunkにカーソルを移動する
git-gutter:next-hunk 1つ次のhunkにカーソルを移動する
git-gutter:stage-hunk 現在のhunkをstageする
git-gutter:revert-hunk 現在のhunkを破棄して元に戻す(undoでrevert-hunk操作をさらに戻すこともできる)
git-gutter:popup-hunk 現在のhunkをdiff形式で見れるバッファをポップアップする(別のhunkに移動すると内容を更新してくれるので超便利!!)

うわ〜これめっちゃ欲しかったやつ!既に入ってたのに気付かなかったーー!

というわけで最初は単純に M-pprevious-hunkM-nnext-hunk に割り当てて使ってたんですが、どうもしっくりこない。というかmetaキーのバインドを二つも占有するのはもったいない。そこでhydraを使っていい感じにしてみました。

;; 前提としてパッケージは全てstraight.elで入れています
(use-package hydra)

(use-package git-gutter
  :custom
  ;; stage, revertで確認を出さないようにする
  ;; (undoでもどせるからいいや、という気持ち)
  (git-gutter:ask-p nil)

  :bind
  ;; hydra-git-gutter起動のキーバインド
  ("C-c g" . hydra-git-gutter/body))

;; git-gutter:popup-hunkをそのまま割り当てるとdiffウィンドウを閉じれないので
;; トグルできる関数を定義
(defun git-gutter:toggle-popup-hunk ()
  "Toggle git-gutter hunk window."
  (interactive)
  (if (window-live-p (git-gutter:popup-buffer-window))
      (delete-window (git-gutter:popup-buffer-window))
      (git-gutter:popup-hunk)))

;; git-gutterのhydra定義
(defhydra hydra-git-gutter nil
  "git hunk"
  ("p" git-gutter:previous-hunk "previous")
  ("n" git-gutter:next-hunk "next")
  ("s" git-gutter:stage-hunk "stage")
  ("r" git-gutter:revert-hunk "revert")
  ("SPC" git-gutter:toggle-popup-hunk "toggle diffinfo"))

これで何が嬉しいかというと、 C-c g でhydra-git-gutterを起動した後、prefixキーなしで連続操作が可能になることです。 具体的には C-c g nnnnnn... とすると n を押す度に git-gutter:next-hunk が実行されhunkを次々と走査することができます( p で戻ることもできる)。

最初にスペースキーでdiffビューワーをポップアップし、hunkを見て行く途中で必要なhunkで s でstageし、いらないhunkを r で破棄する、といったことがスムーズにできるのです。 感覚としては git add -p に近いですが、途中で「あ、ここtypoしてる」とか気付いた時はそのまま編集してからstageに移すことも可能です。

言葉で説明してもいまいち分かりづらいと思うので動画にしました。

ファイル内の差分を整理するという点においてはmagitよりも使い勝手が良いんじゃないでしょうか。

というわけで、二次情報だけに頼ると損するのでちゃんと本家のREADME読みましょう(僕だけか...)

ミクシィChallengeがひとつできるまで - Bug Shooting Challenge #1 を終えて

f:id:nukosuke:20181119182300j:plain

こちらの記事はミクシィグループ Advent Calendar 2018に寄稿しています。
さて、トップバッターのmatsubara0507に続き、2日目を担当します株式会社ミクシィ CRE*1id:nukosukeです。

少し前の話になってしまいますが、11月17日にBug Shooting Challenge (以下、BSC) というイベントを開催いたしました。これはミクシィが毎年数回、エンジニア学生を対象に開催している技術ワークショップ (通称Challengeシリーズ) のひとつです。BSCはサービスの不具合調査をテーマとしたイベントで、今回が記念すべき第1回開催でした。

この記事ではBSCの内容ではなくイベントを開催するまでの経緯や準備に焦点をあてて書きたいと思います。イベント当日の様子を知りたい方は、参加者の学生さんが参加レポートを書いてくださったのでこちらをぜひご覧ください。 (たくさん記事を執筆いただきありがとうございます!)

発端

弊社の人事は、もともとプロダクトの開発に携わっていたエンジニアが所属しているほどエンジニアフレンドリーな文化をもっています。特に新卒エンジニアは採用を通じて人事と仲良くなるためコミュニケーションをとる機会が多く、ChallengeシリーズのルーツともいうべきGit Challengeや、それに続くTDD Challengeもそのような企業風土の中で生まれてきました。

私はGit Challengeの運営に過去2回携わっていましたが、CREチームが'18新卒エンジニアに行った不具合調査研修の好評を受けて人事部からある構想について打診されました。

「この研修を学生向けのイベントとして開催すれば面白いんじゃないかな?」

業界全体にもっとCREを広めたいという想いを以前から抱いていた私にとってこの企画はまたとないチャンスでした。しかし、初めてのイベントをたった1人で形にするのは無理があり、XFLAGスタジオでSRE*2を務める@jtwp470と同スタジオでデータ分析を手がける解析チームのid:sh19910711に協力を仰ぎました。両名はGit Challengeの現役運営メンバーでもあったため半ば強引に引き抜くような形となりましたが、二つ返事でOKしてくれました。さらにプロジェクトマネジメントをCREのグループマネージャーである@otoyo0122に引き受けてもらい、6月中旬にひっそりとBSC運営チームが発足しました。

楽しんでもらうための工夫

研修プログラムを学生向けイベントにしたいといっても、そのまま研修の内容をなぞるだけでは面白みに欠けるのではないかと考えていました。研修というと堅苦しいイメージを持たれてしまう恐れがあったので、ゲーム感覚で参加できるような工夫が必要でした。

また、研修では弊社の人気タイトルのサーバコードに手を加えて教材として使用しましたが、外部の学生さんを招いてのイベントでプロダクトのコードを流用するわけにはいきません。

そこで、茨の道とは知りつつも運営チームで弊社タイトルのクローンを開発することにしました。

この擬似サービスの機能は、

  • ユーザ登録/ログイン
  • ゲーム内通貨の購入
  • ガチャ
  • キャラクター売却

だけのシンプルなものです。

擬似サービスにわざとバグを仕込んでおき、参加者にはあらかじめ用意したログをもとに該当箇所を発見して修正してもらう。

この流れがBSCの大枠として決定しました。

また、イベントの最中はCREになりきって取り組んでもらおうと、ちょっとリアリティのあるシナリオを用意したり、問題文をユーザからの問い合わせメールを模した文面にしたり工夫を凝らしました。

BSCで活きたGit Challengeの運営経験

前述のように、私を含め運営チーム3人にはGit Challengeの運営に携わった経験があったため、ある程度イベントに必要とされるリソースや起こりうる問題を把握していました。BSCではこの経験が活き、必要作業の洗い出しや工数の見積もりをスムーズに行うことができました。

当日のタイムスケジュールはGit Challengeを参考にしており、BSCのために調整を加えていきました。異なるイベント間でも知見が共有され、いわばイベント開催のフレームワークが存在したことが大きな助けとなりました。

本気の環境

学生さんの中には遠方からイベントのために来られる方もいます。貴重な時間を割いてせっかく参加してもらうからには普段触れることのない技術スタックに触れて欲しいと思い、CREチームが実業務で使用しているhadoop + hive環境を使ってログ抽出をしてもらうことにしました。

しかし、そのためにはgrepで検索できるレベルのログでは全く物足りません。そこで、擬似サービスのAPIサーバが完成した後、自動プレイクライアントを作成し、これをEC2上で1週間以上マルチプロセス稼働させることで最終的に12GBのアプリケーションログと8.5GBのリクエストログを生成しました。このクライアントが稼働している傍、私が手動でエンドポイントにリクエストを送り、解答の手がかりになるログを差し込んでいきました。ログ生成の終了予定日に自動プレイクライアントを止め忘れてログファイルがディスクを埋め尽くすという事件が発生したりもしました。

実際のプロダクトではこの比ではないくらい膨大なログがあるのですが、それでもgrepをハングアップさせるには十分なサイズを用意できたため、学生さんにはログ解析における分散処理の必要性を実感してもらえたのではないかと感じています。 (EMRでログ全期間で検索を行うクエリを投げると完了まで1分ほどかかりました)

ログ周りの準備はid:sh19910711がS3バケットに配置したログデータをHadoopファイルシステム(HDFS)用にパーティショニングしたり、そのデータをAWSマネージド分散処理環境であるEMRから参照できるようにしたりして、ログの解析環境を本番さながらに整備してくれました。

当日作業の自動化

当日は打ち合わせや会場の準備などで何かと慌ただしくなることはあらかじめ想定していました。そのため当日行うEMRインスタンスの起動やGitHubリポジトリの作成作業は前日までに自動化しておくことにしました。このあたりの下準備を@jtwp470がTerraformを使って整えてくれたおかげで、当日はプルリクエストをマージするだけで環境を構築することができました。

彼がTerraformと奮闘した記録は後日記事にまとめてくれますので公開されましたらまたこちらで紹介したいと思います。

βテスト: 幻の第0回BSC

一通りの準備が完了すると我々運営チームは完全に油断していましたが、イベント開催日が近づくに連れて「何かやり残した作業はないか」「想定漏れはないか」など誰からとなく不安の声が上がりはじめました。

このような不備を洗い出すためには極力参加者のバックグラウンドに近い人に対してリハーサルを行うことが効果的だと思い、当日チューターとして協力してくれる入社1年目メンバーに対してβテストを行うことにしました。

すると、なんということでしょう・・・

もはや想定通りに進んだことの方が少ないという有様で、具体的には以下のような問題が起こりました。

  • Dockerで用意していたサーバコンテナの起動が失敗する
  • ローカルのHiveコンテナが激重
  • 問題文の説明不足・誤植

これらは不備というより不注意でしかないのですが、フィードバックの中で最も衝撃的だったのは予想以上に問題が難しかったということです。APIサーバやゲームクライアントを設計・開発した当人の「簡単」は全くあてにならず、チュートリアルですら難しすぎるというレベル感でした。

このβテストでは初めてコードに触れる人の立場から意見をもらえたため、その後資料の改善やタイムスケジュールの調整をする上で大いに役立ちました。忙しい中βテストに時間を割いてくれたチューターメンバーには本当に感謝しています。

BSCにかける想い

業務領域の異なる運営メンバー3人がイベントの核となるテーマを模索する中で、共通していたのは「開発者視点だけでなく、ユーザ視点を持ってサービスの運用を考えられるようになってほしい」という想いです。ですから、このイベントを通じて私たちはバグの発見・修正方法だけでなく、その後の対応についても言及しました。

実際に、不具合がおきた時は修正だけで済むケースは少なく、その後に影響範囲を調べ、必要であればユーザに対して補填を行い、原因の説明責任を果たす必要があります。技術イベントだからと単に不具合の修正方法に終始するのではなく「自分だったらどうするだろう」と考えてもらい、マインド面でも何か気づきを得てもらえるように心がけました。

第1回開催を終えて - BSCの今後

まだまだ改善すべきところはたくさんありますが、今回参加してくださった学生さんから楽しかったと言う声が聞けて本当に嬉しい限りです。

BSCは早くも次回開催の話が上がっており、今回見つかった課題や学生さんからのフィードバックをもとにブラッシュアップしていきたいと思います。

Git Challengeの方は第10回を数えるまでの人気イベントとなりました。BSCもこれに続けるよう運営チームともども精進してまいりますので引き続きBug Shooting Challengeをよろしくお願いします。

PR

明日、3日目はcgetcがSpread Sheetを使ったボット開発について何か書いてくれるようです。それでは、引き続きミクシィアドベントカレンダーをお楽しみください!

qiita.com

関連記事

*1:Customer Reliability Engineer: 顧客信頼性エンジニア

*2:Site Reliability Engineer

簡単にできる!Goで書いたCLIツールを配布する方法

どうも、id:nukosukeです。

さっそくですがみなさん、パッとCLIツールを作るとなったらやっぱりGoですか?
Goですよね?

でも、「作ったツールを配布したい」となったときにちょっと困ったことがあります。

  • 開発マシンにGoの環境を作ってる人が少なかった (構築方法説明するのは面倒)
  • Go環境入れてもらったもののバージョンが違ってビルドで死ぬ
  • depやvgoのようなバージョンマネージャを使ってなくて、依存ライブラリがアップグレードしててビルドで死ぬ

とかとか。

前のEmacsの記事でも同じようなことを言いましたが、環境再現性ってやっぱり大事ですよね。この辺りの煩雑さは周辺ツールではなくGo本体のバージョンアップで今後対応が進むようですが、そもそもツールを使うだけの人にビルド環境を用意させるのはどうなんでしょう。バイナリで配布すれば面倒な環境構築も必要ないわけでして。

というわけで、今回の記事ではGoで書いたツールのテストと配布 (CI・CD*1 )環境をサクッと作ります。

要件

  • お金をかけずに誰でもダウンロードできる場所に置いておきたい
  • gitでバージョンタグを打ったら自動でビルドして配布用のアーカイブがアップロードされるようにしたい
  • OSやCPUアーキテクチャごとにクロスコンパイルしたバージョンを用意したい

使用するツール・サービス

GitHub

言わずと知れたGitホスティングサービスですね。今回はツールのバイナリ配布のためにreleaseという機能を使います。releaseでは特定のタグやリビジョンに対してソースコードアーカイブ(.zip, .tar.gz)を自動で作成してくれます。加えて、任意のファイルを追加することができますのでここにバイナリをzipに固めたものをアップロードしていきます。

TravisCI

OSS開発においてお金をかけずにCIを導入したいというときにTravisCIとCircleCIが選択肢に上がるかと思います。(他にもたくさんCIサービスがありますね。おすすめがあればぜひ教えてください)

CI ビルド環境 id:nukosukeの所感
TravisCI VM あんまり複雑なことを考えずサクッと導入したい人向け。TravisCIがサポートしている機能だけで要件を満たせるなら導入がすごくラクGitHub releaseを自動で作る機能など便利機能が満載。
CircleCI コンテナ(v2.0より) テスト環境をカスタマイズしてステップごとに色々やりたい人向け。Dockerイメージを使用してとことんカスタマイズできるため複雑な要件の場合はこちらを選択した方がいい。ステップごとにイメージの切り替えが可能。

今回はTravisCIの機能で要件を満たせるのでこちらを使ってスピーディーにCI環境を作ります。ちなみに今回は触れませんが、WindowsでテストしたいときはAppVeyorが便利です。

gox

Goは GOOS, GOARCH 環境変数を指定することで様々なプラットフォーム向けにビルドすることができます。しかし、いちい環境変数を指定してプラットフォームごとにgo buildを繰り返すのは面倒です。一発で複数のプラットフォーム向けのバイナリを作りたいですよね。

はい、そんなときにgoxが便利です。デフォルトではサポートしているOSとアーキテクチャの全組み合わせでバイナリを出力します。ターゲットを絞りたい場合は、OSとアーキテクチャのリストをオプションに渡すとその分だけビルドすることができます。

使用方法の詳細については後ほど見ていきます。

手順

GitHub: Personal Tokenの発行

releaseページにバイナリをアップロードするのに使います。場所が少し分かりづらいのですが、SettingsDeveloper settingsPersonal access tokens から発行できます。

Generate new tokenで発行できたらTravisCIの環境変数GITHUB_TOKEN という名前で保存しておきましょう。

この際、Display value in build logがオフになっていることを確認しておきましょう。これがオンになっていると、テストのログに$GITHUB_TOKENの中身が表示されてしまうためトークンが漏洩してしまいます。

TravisCI: go testの実行

TravisCIでgo testを走らせる設定です。この記事の本旨とは関係ないですが、パッケージマネージャにdepを、テストカバレッジの計測にCoverallsを使っています。

# 言語を指定します
language: go

# テストを回すGoのバージョンをリストで指定します
go:
  - "1.11.x"
  - master

# 依存ライブラリのインストール処理前に走らせる処理を書きます
#   ここではdepパッケージマネージャとテストカバレッジ計測のためにgoverallsを入れています
before_install:
  - curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
  - go get github.com/mattn/goveralls # for profiling coverage

# Gopkg.toml, Gopkg.lockをもとに依存ライブラリをインストールします
#   installを指定しないとデフォルトではgo getが使用されるため、
#   依存ライブラリのアップデートで突然壊れる可能性があります
install:
  - dep ensure

# テストを実行します
#  2つ目はカバレッジ計測のコマンドです
script:
  - go test -v ./...
  - goveralls -service=travis-ci

自動テスト(CI)だけが目的であればこれで完了ですが、今回は配布(CD)もやりたいので続いてその設定をやっていきましょう。

TravisCI: goxでクロスコンパイル

先ほどの.travis.ymlにビルドの設定を追記します。デフォルトではgoxはカレントディレクトリに {{.Dir}}_{{.OS}}_{{.Arch}} (Windowsの場合は.exeがつく) の形式の名前でバイナリを吐き出しますが、これだとLICENSEやREADMEなど同梱したいファイルがある場合に不便です。そこでディレクトリを切ってその中にバイナリを出力するように -output オプションを指定しています。ターゲットのOSやアーキテクチャはスペース区切りの文字列で渡します。 -osarch="linux/amd64" のように一緒に指定することもできます。

詳しくは gox -h で確認してみてください。

before_deploy:
  # goxをインストール
  - go get github.com/mitchellh/gox
  # Linux, macOS, Windowsに対して、
  # 386(32bit), amd64(64bit)CPU向けのバイナリをビルドします
  - gox -os="linux darwin windows" -arch="386 amd64" -output="{{.Dir}}-{{.OS}}-{{.Arch}}/{{.Dir}}"
  # バイナリを出力したディレクトリをそれぞれzipに固めます
  - |
      for os in linux darwin windows; do
        for arch in 386 amd64; do
          cp README.md LICENSE terraform-provider-zendesk-$os-$arch
          zip -r terraform-provider-zendesk-$os-$arch.zip terraform-provider-zendesk-$os-$arch
        done
      done

TravisCI: GitHub releaseページにバイナリをアップロード

さらに、GitHub releaseページにバイナリをアップロードする設定を追記します。

deploy:
  provider: releases
  api_key: $GITHUB_TOKEN
  file_glob: true
  file: terraform-provider-zendesk-*-*.zip
  skip_cleanup: true
  # tagが打たれたときのみGo 1.11でビルドしたバイナリをアップロードします
  on:
    tags: true
    go: "1.11.x"

ではさっそくgit tagでバージョンタグを打ってみます。しばらくして、releaseページに以下のように.zipがアップロードされていたら成功です。

それでは、楽しいGoライフを!

*1:CI = Continuous Integration, CD = Continuous Delivery

Emacsパッケージマネージャ決定版:これからのパッケージ管理はstraight.elで決まり!

ども、久々のブログ更新は最近導入したstraight.elというEmacsのパッケージマネージャについて書きます。

7年ほどEmacsを使い続けてきましたが、まだまだ初心者の域を抜け出せずにいる id:nukosuke です。

前置き

全世界76億人のEmacs使いの皆さんはどうやってelパッケージを管理していますでしょうか。
僕はこれまでpackage.elやCask、el-getなどのパッケージマネージャを使ってきましたが、どれも好きになれませんでした。

というのも、僕がパッケージマネージャに唯一求めるのはマシンを買い変えた時の環境再現性(reproducibility)であって、今まで触れてきたものはこれを担保するためにかなりの労力を費やす必要があったからです。

具体的には、

  • package.el : そもそもパッケージのバージョンをロックする機能がない・・・
  • Cask : python 2系に依存していたためにpython 3に切り替えた途端に動かなくなった・・・
  • el-get : リビジョンハッシュを指定しないと最新版のパッケージがインストールされてしまい、いつのまにかパッケージ同士の互換性がなくなっていた・・・

と、パッケージマネージャごとにいろいろな苦しみがありました。


追記 2018/4/11:
id:taraoさんからコメントでel-get-lockというパッケージを教えていただきました。
el-getではこれを使うことでロックファイルを生成することができるようです。


それでもEmacsを愛していたのでなんとかやってこれましたが、そろそろ限界。
そんなときに会社の同期にSpacemacsを勧められました。(straight.elじゃないのかよ)

こういった大きなディストリビューションにはあんまり興味がなかったので最初は乗り気ではなかったのですが、ちょっとだけ触ってみようと思ってインストールしてみたら、これがわりと良い。

設定要らずで必要な機能はほぼ揃うし、ファイル拡張子から必要なモードを自動的に判断してlayer(設定のまとまり)を勝手に設定ファイルに追記するという変態っぷり。layerはSpacemacsのリポジトリホスティングしているので突然設定が壊れることもありません。最高やないか。

そんなこんなで特に不満を抱くこともなく、気づけば1年弱使っていました。




でも、気づきました。

だんだんEmacsへの愛着が薄れている事に。





これだったらAtomでもVSCodeでもなんでもいいじゃないか!(あえてあいつの名前を出さない勇気)

そもそもEmacsを好きになったのは自分の好みを投影できるからであって、
なんというか・・・こう・・・大衆のイメージに染まっていない地下アイドルを応援するような気持ちです。

と、前置きが長くなりましたがそういうわけでもう一度、秘伝のタレ探求に乗り出したわけです。

straight.el

github.com

このパッケージマネージャを知ったのはかなり最近です。どういう経緯で行き着いたのか全く覚えてないのですが、気づいたらブックマークに入っていました。

Next-generation, purely functional package manager for the Emacs hacker.

なんともキャッチーじゃないですか?

READMEには他のパッケージマネージャとの比較が書いてあるので、ニーズに応じてどれを使うべきか判断する材料にもなります。

そして、原則にはしっかりと 100% reproducible と書いてありますし、説明を読めば読むほどこれが長年探し求めていたものだと思いました。

しかし、残念なことに以前管理に苦しんでいたときにはこのパッケージマネージャはまだ存在しなかったのです。最初のバージョンがリリースされたのは2017年1月。

つまり最先端なのです。

メリット

straight.elを使うメリットはたくさんあります。
このエントリでは、その中から僕がこのパッケージマネージャを気に入った主な理由を2つ挙げます。

use-packageマクロをstraight.elにフォールバックできる

use-packageはパッケージのrequireやら、load-pathの設定やら、キーバインドの設定やら、その他諸々をひとまとめにできる強力なマクロです。 るびきち先生のこちらの記事でも紹介されているように、use-packageを使うと設定ファイルの可読性が一気に上がります。

また、use-package自体にはパッケージをインストールする機能はないのですが、読み込み時にパッケージが存在しなかった場合に別途パッケージマネージャを使ってインストールする処理にフォールバックすることができます。

そして、use-packageのREADMEには「フォールバック先にはデフォルトでpackage.elを使用するが、use-package-ensure-function を上書きすることで別のパッケージマネージャを使うことができる*1」と記述があります。

いわく、現在*2この拡張に対応しているのはstraight.elだけとのことです。

これでuse-packageが無敵になりました。

lockファイルを生成できる

はっきり言って導入の決め手はこれです。Caskやel-getでもバージョンを固定することはできましたが、自分で指定する必要がありました。

でもstraight.elを使えば、もう温もりの手作業でリビジョンハッシュを設定ファイルに書き込む必要はないんです。

M-x straight-freeze-versions を実行すると ~/.emacs.d/straight/versions という名前のlockファイルが生成されるので、これをgitで管理しておくことで、設定を行ったときと同一のバージョンでパッケージ構成をいつでもどこでも再現できるのです。

設定例

ちゃんと仕様を知りたければもちろん公式のREADMEを読んだ方がいいです。
ここでは僕がstraight.elを使い始めるにあたって最初に行った設定を紹介します。

;; 何も考えず公式のREADMEからコピペすればいいコード
;; straight.el自身のインストールと初期設定を行ってくれる
(let ((bootstrap-file (concat user-emacs-directory "straight/repos/straight.el/bootstrap.el"))
      (bootstrap-version 3))
  (unless (file-exists-p bootstrap-file)
    (with-current-buffer
        (url-retrieve-synchronously
         "https://raw.githubusercontent.com/raxod502/straight.el/develop/install.el"
         'silent 'inhibit-cookies)
      (goto-char (point-max))
      (eval-print-last-sexp)))
  (load bootstrap-file nil 'nomessage))

;; use-packageをインストールする
(straight-use-package 'use-package)

;; オプションなしで自動的にuse-packageをstraight.elにフォールバックする
;; 本来は (use-package hoge :straight t) のように書く必要がある
(setq straight-use-package-by-default t)

;; init-loaderをインストール&読み込み
(use-package init-loader)

;; ~/.emacs.d/init/ 以下のファイルを全部読み込む
(init-loader-load "~/.emacs.d/init")

これであとは ~/.emacs.d/init/ いかにそれぞれのパッケージの設定ファイルを置けばいいだけになりました。

例えばcompanyの設定ファイルはこんな感じです。
インストール、読み込み、変数の設定からキーバインドまで一挙に行うことができます。

(use-package company
    :init
    (setq company-selection-wrap-around t)
    :bind
    (:map company-active-map
        ("M-n" . nil)
        ("M-p" . nil)
        ("C-n" . company-select-next)
        ("C-p" . company-select-previous)
        ("C-h" . nil))
    :config
    (global-company-mode))

さいごに

新しいということもあってか、これまでのパッケージマネージャに抱いていた不満がstraight.elでは全て解消されていました。

これを使って設定ファイルを書き直してからまだ一度も壊れていませんし、そもそも100% reproducibleの言葉どおり、毎回同じバージョンが降ってくるので壊れようがありません。プライベート用のPCで作った設定を編集することなく、仕事用PCで全く同じ環境を作ることにも成功しました。

僕のようにいろんなパッケージマネージャを渡り歩いてきた方、 特に環境移行の際に設定ファイルのメンテナンスコストに悩まされているという方はstraight.elを試してみてはいかがでしょうか。