NixOSを最新にして、日本語入力にfcitx5を使うようにした

手順

今回はNixOS 22.11から23.05を飛ばして23.11にアップグレードする。

基本的に毎回そうだがリリースノートを読んで、/etc/nixos/configuration.nixを変更する。

通常の場合、NixOSのマニュアルに書いてあるとおり、チャンネルを新バージョンに切り替えてから、 nixos-rebuild switch --upgradeを行うが、今回はリリースノートにnixos-rebuild bootを使う旨が書いてあるのでnixos-rebuild boot --upgradeでアップグレードを行い、その後、再起動をする。(switchとbootの違いはヘルプ参照)

日本語入力

fcitx4を使っていたが、NixOS 23.05から削除されたのでfcitx5に移行する。

NixOSのwikiに書いてあるとおりに以下のように設定した。

i18n.inputMethod = {
    enabled = "fcitx5";
    fcitx5.addons = with pkgs; [
        fcitx5-mozc
        fcitx5-gtk
    ];
};

しかし、私はデスクトップマネージャーを使わずにi3を使っており、fcitx5自動起動せず、 自分で起動しないと日本語入力が有効にならない状況だったので、 services.xserver.desktopManager.runXdgAutostartIfNone = true;を追加した。

これにより、期待通り動作するようになった。

私のようにデスクトップマネージャーを使ってない人はこの設定が必要になるかと思われる。

オプションの詳細はNixOS Search - Optionsで確認ができる。

services = {
  xserver = {
    ...(その他の設定)
    desktopManager = {
      ...(その他の設定)
      runXdgAutostartIfNone = true;
    };
  };
};

PureScript packageのアップデートが自動でpackage-setsに反映されるようになってた

プロローグ

2年弱ほど前にv0.14.x対応をしてからしばらくPureScriptから離れていたが、急にやる気が出てv0.15.xを対応していた。 しばらく離れていたのでおそらくv0.15.xで動かなくなったのであろうライブラリが全てpackage-setsからいなくなっていて、 あ〜、もっかい追加しなきゃな〜めんどくさいなぁと思っていた。 (注: 現在主流のspagoというパッケージ管理兼ビルドツールでデフォで使えるライブラリにするには素手でpackage-setsで管理されてるdhallに変更を加えてPRを送る必要があった)

そんな気持ちでアップデート作業をしていたところ、なんか勝手にpackage-setsに反映されていることに気づいた。

Registry

purescript/registryというものがつくられ、そこを中央registryにしような!みたいな流れがありつつ、そのregistryによって何かが動作するみたいなことは長い間何もなかったものの、時を経て、どうやら現在は以下のように動いているようである。

  • 新しくライブラリつくった時はregistryに情報を追加してね
  • そうしといてくれれば、ドキュメントのpursuitへのもアップロードしとくし、package-setsにライブラリを追加しといてあげるよ
  • 一度registryに登録しといてくれれば、タグのリリースあったら勝手に諸々更新されるよ

purescript/registryからはライブラリは消えてなかったので、 確かになんもしてなくても勝手にpursuitへのアップロードはされてて、package-setsにも追加されていた。 非常に少しずつではあるが便利にはなってきてはいるな〜という感じではあった。

ただ、なんだかよくわからないけど、package-setsへの追加だけ失敗しているライブラリが一部あった。 追加されてないライブラリを見ると、direct dependenciesが全て入ってないライブラリ(要するに任意のライブラリに依存Aがあり、Aは依存a', b'をもつ時に、任意のライブラリがa'を直接参照していたら、依存にAが入っていれば問題なくビルドはできるけど、a'も直接実装で参照されてるからa'も依存としていれろ的なアレである。現在のspagoとかはそれをいれないとエラーになる。)が追加されてなかったので、そういう理由かと思ったが、後日見ると、direct dependenciesが全て入ってなくてもpackage-setsに追加されていたので、 結果的になにが理由だったのかは謎だった。 なんにせよ、v0.15.x対応がされているのにpackage-setsに入ってないものはありそうだなぁという感じだった。 というか実際に他人がオーナーのライブラリでもそういうのがあったので、対応した。

定期実行はpurescript/registrygithub actionを通して、purescript/registry-devのコードを実行していたので、それを読めば謎はわかるだろうが、めんどくさいから読んでいない。

package-sets

元々使われていたpurescript/package-setsは今も生きているが、purescript/registryにもpackage-setsというディレクトリができていてそこで管理がなされているので、そのうち、完全に移行がされるような雰囲気がある。(ちなみに、文中のpackage-setsに追加されてないライブラリが一部ある、みたいなトラブルが起こったのは前者の昔からある方である。)

現状のspagoはpurescript/package-setsのほうを使っている。

エピローグ

ゆっくりではあるものの、esmodule対応とか、パッケージ周りの整備が知らない間に結構進んだなという小学生並みの感想を残してフィニッシュです。

近況

2021/5から会社員に戻りました。 仕事のオファーはなんだかんだ尽きないし、フリーランスは続けられそうでは有りまして、 しばらく会社員に戻るつもりはなかったものの、かくかくしかじかで会社員に戻りました。

どんな会社かと言うと、SOELUという会社で、オンラインのヨガを提供している会社です。 この会社はフリーランスとして関わっていたのですが、そこに晴れて正社員として関わることになりました。

決め手をあげると(生々しくないやつを除いて)、

  • 週4勤務でいいよってなった
    • 一見私にのっぴきならない事情がありそうですが、残りの1日で趣味とかやりたいという話をしました。
    • こういった事由でも少なくともきちんと検討はしてもらえる柔軟さがあります。
  • 採用活動にも少し関わっていて、続けざまにいい人材がとれた
  • 分野内では弊社はイニシアチブがある

などのギョーム的な良い流れがあったことに加えて、 社内にポーカーをやる人がいて、スプリント終わりにトナメが開催される、という点も良かったです。 私はポーカーが大好きなので、ギョームには関係ないですが、QoLがあがるため、この点も非常に魅力的でした。

仕事自体もビジネス的にもシステム的にもやることがたくさんあるので暇しなそうなところも良かったです。

そんなSOELUですが、正社員のネイティブアプリのエンジニアが不足していまして、たぶんネイティブアプリのエンジニアは特に歓迎されると思います。

転職を考えている方はノリで一回弊社に話を聞きに来ても良いかもしれません。

そういうわけで、とりあえず元気にやっています。

正社員になったので、小規模企業共済をどうするかなど未だに悩んでいますが、 とりあえずはしばらくサラリーマンとしてやっていきつつ、宝くじでも買おうかなという所存です。

NixOSで各言語のパッケージをNix経由での管理にせずに開発環境を作る

NixOSでは共有ライブラリの管理方法が他のlinuxディストリビューションと違い、/nix/storeに置いてある。 よって、例えば無邪気にnpm installでいれたcliがnot found扱いとなる。 なのでベーシックなやりかたとしては、Nixを使って各言語のパッケージも管理するというのがあって、 patchELFでpatchをあててinterpreterとrpathをセットし直すという形でderivationを書いたり、あるいは、そういった形でNixのパッケージとして公開されている。

ただ、Nixで各言語のパッケージを管理したくない気持ちというのはまぁあって、普通にnpmで管理して、雑にnpm installして動く、みたいなのがいいとおもうこともあるはずだ。

今回はelectronをリポジトリローカルなモジュールとして管理したかったというシチュエーションだったので、この条件を満たすshell.nixをつくった。

{ pkgs ? import <nixpkgs> {} }:

with pkgs;

let
  sharedDeps = atomEnv.packages ++ [
    libdrm
    libxkbcommon
    mesa
  ];

in
  (buildFHSUserEnv {
    name = "electron-test-env";
    targetPkgs = pkgs: sharedDeps ++ [
      nodejs-12_x
    ];
  }).env

やっていることとしてはnodeの依存とelectronの実行に必要な共有ライブラリの依存が入った環境をbuildFHSUserEnvを使って用意している。

shell.nixを用意する場合、mkDerivationmkShellを使うと思うが、その場合は、共有ライブラリの検索のためのコード(LD_LIBRARY_PATHの設定、あるいは、rpathのパッチ)とELFのインタープリタのパッチのコードが必要になってしまう。

しかし、buildFHSUserEnvを使うと、FHSに準拠した形で環境を用意してくれるので、必要なライブラリを依存に入れるnixファイルを書くだけで済む。

こういったshell.nixをプロジェクトルートにでも置いて、nix-shellを呼び出すだけで、あとは各言語のパッケージ管理ライブラリのみでパッケージを管理できる。(このnix-shellで起動したshellの中なら、無邪気にnpm installしたcliも動く。今回の場合はelectronのcliが問題なく動く。

結果的にはほとんどDockerfileを書いているのに近い感覚で開発環境を作ることができる。

すげえ便利なので、NixOSユーザーはbuildFHSUserEnvを使ってみると良いでしょう。

なお、詳細はNixOSのマニュアルにあるので、使う前は要チェックです。

ULIDのPureScript実装作りました

背景

ULIDのPureScript実装がないのでそのうちつくろうという意思だけ持っていたものの、その後半年以上経過し、気づいたら他の人に先に作られてた。 しかし、それはnpm packageのulidに依存しており、こんなのまでnpm packageに依存するのは嫌なので、やっぱり自分でPureScriptで書くことにした。

ULIDとは

端的に言えばソートできるUUIDみたいなもんです。 ULIDには時刻を表す部分とランダムに生成される部分があるからです。 その性質上RDBで用いる時にフラグメントしないです。 詳しくはこちら

成果物

purescript-simple-ulid

このブログを書く時点ではpackage-setsにPR出している最中なので、package-setsに入ってないかもしれません。

補足

Monotonic ULIDはつくってません。

名指しでやれ

自分のアウトプットを人にチェックしてほしいとき、というのは誰にでもあるし、仕事をしていれば日常的に観測できる事象である。

私はプログラマをしているが、プログラマの場合であれば、例えば、本番のデータを修正するスクリプトを他人にチェックしてもらうとか、単純にPullRequestをレビューしてもらう等だ。

この時に、不特定多数に対してチェックを呼びかけるというケースを観測することがある。 例えば、slackでのやりとりであれば、@channel等の不特定多数にたいするメンションのみを用いたやりとり、Pull Requestであれば、複数の人間をレビュアーに設定する、等である。

こういった不特定多数にチェックを求める行為をする癖がある人がいたらすぐにやめたほうがいい。

理由は明確で、チェックしないで無視する、あるいは、適当にチェックしてレスを返すという流れを生む確率があがるし、実際みんな基本無視で、レスをしていてもめっちゃ適当、という事例を見ているからだ。

他の人が見てる、自分が返さなくていいという前提になってしまうので、無視しやすいし、無視しなかったとしても真剣さのないチェックになって適当にレスを返しやすい。

きちんとチェックをしてもらいたい意思があるなら、多数にお願いをすること自体はいいが、それだけでは絶対に駄目で、必ず生贄を一人だけ指名する必要がある。「あなたがメインのレビュアーです」「あなたがチェックしないなら先に進みません」ということが相手に伝わらないといけない。

チェックをお願いされた側は、直接指名は自分一人だけなので「ちゃんとしなきゃ」と考えやすくなる、少なくとも無視はされなくなる。

人にやってほしいチェックがあったら、必ず名指しで生贄を捧げろ。もちろん生贄はちゃんとチェックしてくれそうな人、できそうな人を選びましょう。

RecoilにインスパイアされてPureScript用仮想DOMつくった

追記

ここに記述した内容は既に古くなっています。 内部実装は既に書き直されていますし、Hooks likeなAPIも少しだけインターフェースが変更されています。 興味のある方は直接purescript-grainへ見に行ってください。

動機

Recoilが思ったより話題になっていたので、PureScriptだったらどういう表現ができるかなと思ってつくりたくなった。

そういうわけで、PureScriptにおける仮想DOM実装の3作目が爆誕してしまった。

結果

purescript-grain

こういう感じになった

型クラスのインスタンスにした型が各状態になり各状態キーにもなるという感じになった。 実はここの表現に関してはかなり迷って、最初は、シンボル(型レベル文字列)と状態の型をもち、キーの型から状態の型への関数従属性をもった型クラスのインスタンスにすることで表現しようと考えたが、PureScriptはOrphan instance禁止なため、シンボルはどんなシンボルでもシンボルというひとつの型、且つ、上記の関数従属性があるので、型クラスの宣言と別のモジュールでインスタンスの宣言をするとコンパイルが通らない。 よって、そこから少し右往左往してこの形に落ち着いた。

グローバルステート

以下のように任意の型をGlobalGrainインスタンスにし、状態の取得・購読や更新関数の取得をプロキシ経由で行う。

その型のコンストラクタ関数の参照を型の参照とみなし、状態のキーとして扱い、一意に識別する。

各所で勝手に宣言された状態は、ひとつのオブジェクトにおしこまれるようになっているため、全状態を見たりできるdev toolを作ろうと思えばつくれる状態にはなっている。

ただ、現状は型の参照をキーにして、uuid v4ベースの文字列を管理し、その文字列を内部で状態のキーとして使っているため、ヒューマンリーダブルではない。

これに対するアプローチは状態名のヒューマンリーダブルな表現を登録してもらうか、あるいは、型からモジュール名を含めたシンボル(型レベル文字列)をつくる、みたいな、コンパイラが解決する特殊な型クラスがあったりするといい。

後者の場合は、以下のtypeRefOfの実装もしなくて済むようになる。 後者の案に似たような提案はされているのでそういった機能が入ることを0.1%くらい期待しているが、あまり期待できない。

前者の案を採用する場合は、所詮表示用なのでユニークである制約はつける必要はないが、まぁだるいといえばだるいので、結局、現状はヒューマンリーダブルな名付けはしなくていいようになっている。この課題に関しては先延ばしにするということにした。

import Prelude

import Grain (class GlobalGrain, GProxy(..), VNode, fromConstructor, useUpdater, useValue)
import Grain.Markup as H

newtype Count = Count Int

instance globalGrainCount :: GlobalGrain Count where
  initialState _ = pure $ Count 0
  typeRefOf _ = fromConstructor Count

view :: VNode
view = H.component do
  Count count <- useValue (GProxy :: _ Count)
  updateCount <- useUpdater (GProxy :: _ Count)
  let increment = updateCount (\(Count c) -> Count $ c + 1)
  pure $ H.div
    # H.onClick (const increment)
    # H.kids [ H.text $ show count ]

動的なアイテムに対するグローバルステート

なにか動的なアイテムにそれぞれに対して、状態をつくりたい、要するにUIへの通知範囲を極端にしぼりたいケースにも対応している。

以下は簡単な例である。 この場合は、プロキシの値がもつキーと型への参照を用いてアイテムごとの状態キーが作られるようになっている。

import Prelude

import Grain (class KeyedGlobalGrain, KGProxy(..), VNode, fromConstructor, useUpdater, useValue)
import Grain.Markup as H

newtype Item = Item
  { name :: String
  , clicked :: Boolean
  }

instance keyedGlobalGrainItem :: KeyedGlobalGrain Item where
  initialState (KGProxy key) = pure $ Item
    { name: "Item " <> key
    , clicked: false
    }
  typeRefOf _ = fromConstructor Item

view :: String -> VNode
view key = H.component do
  Item item <- useValue (KGProxy key :: _ Item)
  updateItem <- useUpdater (KGProxy key :: _ Item)
  let onClick = updateItem (\(Item i) -> Item $ i { clicked = true })
  pure $ H.div
    # H.onClick (const onClick)
    # H.kids [ H.text $ item.name <> if item.clicked then " clicked" else "" ]

その他

この記事の本筋とは関係ないので書かないが、ローカルステートもある。 また、FFIでnpmパッケージを使っておらず全体的にPureScriptで書いてある。

また、2作目につくった仮想DOMは、でかいひとつのグローバルステートしかもてない制約と一部グローバルステートしか持てないことで不便になるケースに対応するための仕組みをいれた形のものだが、多数のファイルが型レベルでグローバルステートに依存する設計になっており、ステートに手を加えると、大量のファイルが再コンパイルされるので、 コードベースがでかくなってくると差分ビルドしてても重い。 というかそういう状況が発生し始めていた。

3作目のpurescript-grainは各グローバルステートの宣言をモジュールに分けておくだけで、仕組み上同じ問題は起こらない。

また、仕組み上、本家のReact hooksと違い、呼び出し順序が変わろうが、呼び出したり呼び出さなかったりしようが、バグらない。

既に少し派生パッケージをつくってみたが、書き心地としては悪くない。(自分が作ったので自分がそう思うのは当たり前だが...

なお、package-setsに追加したものの、まだpackage-setsがリリースされてないし、今日もさらにアップデートしたので、 試したくなった場合は、packages.dhallのadditionに手動で加えてもらう必要が有ります