"tech"

2025-01-17

2025-01-16

今日はたくさん設計をしたので満足。

今週は電車でA Philosophy of Software Designを読んでいる。 2023年3月8日にAmazonで注文したらしいので、入社前の時期に現実逃避で読んだのだろう。当時はあまりプログラミングをしたり、ずっと動き続けるシステムを設計したりする経験がなかった。なので実感を伴った教訓とは思えず、知識としてさらう感じだった。それはそれで良かったし生きてきたのだけど。 2024年の年初に配属されてソフトウェアをいろんな人と長く作ってきたことでいろんな辛みを体感できた。なので今読むアドバイスは経験を思い出しながら読む観点でも面白いし、まさに今考えていることへの一つの意見としても面白い。

だんだん意見を通すのが簡単になってきたので、自分が思う良いことを主張して取り入れて、楽しいプログラミングを布教しようと思う。

そして当時に比べてペーパーバックの値段が1.5倍になっていて驚き。

コードレビューとか設計レビューで直感的によくなさそう、こうしたい、と思うケースはもしかしたらこの本から得た知見なのかもしれないと読み返していて思う。

何かの魔法で一緒に働くエンジニアが熱中して僕の選んだ本を一冊読んでくれるなら、これを読んでほしい。

いい音がする文章が気になる。僕の小説の好みは文体に左右されると感じているので。なので作者が好きなら大体のその人の作品が好き、みたいなことがよくある。

2025-01-13

昨日は友達とキリンビール工場に行ってきた。500円のツアーがあってビール作りの概要を教えてもらいつつビールを飲ませてもらえる。だいぶ楽しかった。あの体験は優しさで成立していると思う。

原料となる麦やホップを食べさせてもらえたほか、ビール製造の中間生成物である麦汁の飲み比べをさせてもらえた。何より説明してくれるお姉さんがビール好きで、1つ質問すると厳選した3つを返してくれるのが良かった。一緒に飲んで欲しい。飲ませてもらったビールもめちゃくちゃおいしいし。併設されているレストランに行けていない(リサーチ不足でご飯を食べてから行ってしまった)のと、他にも見学コースがあるみたいなのでまた行きたい。

ホップを使ったリンスを買って使ってみたら、髪からフルーティ系なビールの匂いがして最高。

AIに仕事を奪われることはないだろう。とはいえ単に作業をするだけの労働はAIに奪われる。コードを単に書くのは作業であって、仕様からコードを生み出すだけならAIでやった方が早いし(今の相場なら)安いから。

とはいえそんな単純作業が仕事であることはなくて、将来のこととか仕様の考慮に入っていなかったことをコードを書きながら調整して主体として責任を持って成果を出すことは、社会がまだAIに対して認めていないように感じる。

誰もが優秀な部下を持つようになったが、人々は誰かに責任を持った主張をして欲しくいのだろう。ポジションをとって主張をし、ダメだったときは失落したり挽回することを人格に求めている。信頼をして仕事を任せる相手は人間じゃないと嫌だと社会が思っている。

そういうわけで人間は労働力としての生成AIを手に入れただけであって、人間社会で主体として認められたあらたな人格を手に入れたわけではない(今のところは)。成果を出すためには社会が理解できるポジションを取ることが必要で、そのロールモデルの振る舞いとして社会と交流しないといけない。どんな振る舞いをするのが良いか決めるために生成AIを駆使する仕事の仕方が増えるだろう。ロールモデルには矛盾なくとりうるアトミックな行動があって(思想とか哲学みたいなもの)、それを組み合わせることでその人らしい振る舞いが形成される。思想とか哲学とかに一貫性がないと社会から信頼してもらえない。一方で外部から同じ要求がなん度もくることはないため、それに応じた異なる振る舞いをする必要はある。思想や哲学から外部刺激に対して振る舞うことにはエネルギーとか思考とかが必要で面倒だから生成AIを使うと便利だろう。

これはプログラミングにも応用できる。アプリケーションのコアなロジックのインターフェースは綺麗に設計されてクライアントから受け入れてもらう必要がある。そこがブレると利用するのがそもそも大変だし、見出した価値がすぐに失われてしまうだろう。一方で、それを活用して振る舞いを追加したり修正することは生成AIがこなすだけで十分なのではないだろうか。振る舞いを追加することは、そのアプリケーションのコアな部分のインターフェースと世の中によく知られた技術要素のインターフェースを組み合わせて表現できるだろう。

外部からの要求に応答して振る舞いをしたりインターフェースを利用して新たな機能を提供する際に、コアな思想を見直してアップデートすることもあるだろう。とっているポジションをちょっと変えるということ。使う側からすると理解の仕方をちょっと変えないといけないのでこの変更が気に食わなかったり理解できなかったりすると嫌な感じがする。変化の速度が早すぎて理解が追いつかないとたとえ良い変化であったとしてもアンチになってしまい信頼をなくすことに繋がる。そのため人間に直接理解されるインターフェースは人間に合わせた進化をしないと社会に適合できなず、市民権を失う。

したがって生成AIはインターフェースを提供するソフトウェアエンジニアの職業を奪うことはない。一方でインターフェース設計に関与しない、定義されたインターフェースの中だけで作業をして、他人と相互作用をするインターフェースを定義しないエンジニアリングは生成AIのコストに負けて自動化されるだろう。

次に、他人と相互作用するインターフェースのあり方が変化するかが問題になる。これまで公開されて使われていたようなインターフェースは今後も必要だろうか。インターフェースを解釈するのが生成AIだけになれば、その変化が早くても理解が追いつくためわざわざポジションをとったり人間の理解速度に変化の速度に合わせる必要がなくなる。これは作り上げるものの抽象度の違いに依存するだろう。何かを作り上げたかったらその一段下の抽象度の言葉で構成を説明してほしい。妥当性を検証するために。そこから下は信じることにして(信じられる何らかの根拠を持つことになる、ここも何か困難があるかも、今は著名なOSSだからokとか言って信用することにしている)、欲しいものの一段下のレイヤで自分で納得がいけば良いものであると判断する。

そして、人間の思考とか議論のレイヤは高々10とかにおさまっており、それが生成AIの登場で変わるとも思わない。なのでインターフェースを人間が作ってポジションをとる必要性も今後変わらないと思う。根拠はなくて直感だし、多分僕の願望も入っっている。

良いインターフェースを定めるために、鍛錬を積む必要はあり、それは生成AIによって取って代わられる仕事でなされることが多い。生成AIを使って仕事をやる先端のインターフェース提供者と、そこから学べない後進が場所によっては生成AIがやる仕事をするローカライズされたインターフェースの提供者が並行して存在する世界になるだろう(生成AIに限らずこの状況はあるだろうが)。

wip Next.jsでのテストを自由自在に書けるようになりたい

Next.jsのアプリケーションでテストを自由自在に書けるようになることが目的。

vitestを用いる。アサーションにはexpectなどを使ってマッチャを指定する。 expectや使えるマッチャはhttps://vitest.dev/api/expectが詳しい。

  • テスト戦略の類型とトレードオフの把握
  • ユーザインタラクションの再現

2025-01-11

  • 日記管理CLIからhugo依存を抜きたい。Hugoはもう使わないので単にインストールの必要があり面倒になっている。テンプレファイルが好みじゃないのもある
    • tools/daily を拡張する。名前も変えてフルリプレイスでも良い。仕様を決めよう。
  • ZedのAIと仲良くする機能を把握したい
    • した。便利。日記全部読ませるとめちゃくちゃ面白い。
  • Zedにコントリビュートできるようになっておきたい
    • お作法
    • アーキテクチャの把握
    • プログラミング能力
  • CLIたくさん作りたい。ただプログラミングしたい
    • ちょっと作って満足した
  • フロントエンドのテストを完全に理解する
  • 構文、意味、契約みたいな学生の頃は一般的だと思っていたけど世の中ではそうじゃなさそうな概念を説明して、みんなに抑えてもれえるように主張する
  • 掃除する。部屋がめちゃくちゃ汚いので
  • ルンバのメンテナンスをする。なぜか秒でダスト容器がいっぱいになったと判定される
    • ダスト容器のセンサーがあってそこを綺麗にしたので多分解決する。フィルタを水洗いしちゃったのでそれを乾かしてから動かしてみる
    • 初めてだったゴミを集める周辺は簡単に分解できた

Zedの機能を把握したことでAIを使ったコーディング能力が上がった。全体的なリファクタリングを受け入れるのはきついので、ロバストなコンポーネントを作り上げる必要がある。それらを全て把握して、気合いで組み立てる体験が最高。モデリング・抽象化・契約が効いてくるのではないだろうか。

https://zed.dev/blog/zed-ai これが読み途中。

漏れなくスタックトレースをGoで取りたい!

k1LoW/errors でスタックトレースをエラーにつけられるようになる。便利なのだがトレースをプログラマが明示的に指示しないといけない。設定をコードベースで一回やれば終わりではなく、エラーの発生源で書かないといけない点が気になっている。書くこと自体は許容しているが、漏れがないように人間が頑張るのは許容したくない。漏れをなくすための仕組みを主張する。

ライブラリとして使うためにはreadmeの説明で十分だと思うが、議論のためにここでも説明する。以下のように用いる。

https://go.dev/play/p/Cwp4n-vZONv

package main

import (
	"encoding/json"

	"github.com/k1LoW/errors"
)

func f() error {
	// このようにWithStackを呼び出した箇所でのスタックトレースがerrorオブジェクトに記録される
	return errors.WithStack(errors.New("ouch!")) // ここは11行目
}

func main() {
	err := f()
	// スタックトレースを含んだエラー内容をフォーマットする
	s := errors.StackTraces(err)
	b, _ := json.Marshal(s)
	println(string(b))
}

実行すると以下のような出力が得られる。見やすさのために jq にかませてフォーマットした。

[
  {
    "error": "ouch!",
    "frames": [
      {
        "name": "main.f",
        "file": "/tmp/sandbox123757503/prog.go",
        "line": 11
      },
      {
        "name": "main.main",
        "file": "/tmp/sandbox123757503/prog.go",
        "line": 15
      },
      {
        "name": "runtime.main",
        "file": "/usr/local/go-faketime/src/runtime/proc.go",
        "line": 272
      },
      {
        "name": "runtime.goexit",
        "file": "/usr/local/go-faketime/src/runtime/asm_amd64.s",
        "line": 1700
      }
    ]
  }
]

スタックトレースの先頭 (frames配列の先頭要素) はWithStackを呼び出した位置を指す。

したがって、アプリケーションの中でのエラーの発生源をスタックトレースから漏れなく特定するためには、エラーの発生源の全てでWithStackを呼び出す必要がある

なお、WithStackを重ねて呼び出しても問題ようになっている(深いスタックトレースを持つものが生き残るようになっているし、他のも気遣いがされている)。

名前付き返り値とdeferを使って忘れないようにする手法も紹介されている

func f() (err error) {
	defer (func(){
		err = errors.WithStack(err)
	})()
	b := strings.Builder{}
	_, err := b.Write([]byte("hello"))
	if err != nil {
		return err
	}
	// ...
}

ある程度楽をできるし、この後の議論をした上でもバランスの良い選択肢だと思うが課題も感じている。その課題と解決案を以降で議論する。

  1. deferを書くのを忘れそう
    • こちらはリンターでなんとかなるだろう。ここでは深く議論しない
  2. deferの中でerrors.WithStackを呼ぶと、エラーの発生源から離れるため、具体的にどのreturn errで落ちたかがスタックトレースから追えない
    • エラーメッセージから判断できる可能性は大きいけど

二つ目の課題の例を以下に挙げる。fではどの行で落ちたか分からないが、gではどちらのwithStackで生成したかが残る。

func f() (err error) {
	defer (func() {
		err = errors.WithStack(err)
	})()
	if err := ok(); err != nil {
		return err
	}
	if err := ng(); err != nil {
		return err
	}
	return nil
}

func g() (err error) {
	if err := ok(); err != nil {
		return errors.WithStack(err)
	}
	if err := ng(); err != nil {
		return errors.WithStack(err)
	}
	return nil
}

func ok() error { return nil }
func ng() error { return errA }
var errA = errors.New("this is error")

https://go.dev/play/p/SGr1B4sDF9I で動かせる。

k1low/errorsでは、errors.WithStackはfunc(error) error 型を持つ。それを func(error) errors.T とする。ここでTは以下のようなerrorをStackTraceメソッドで拡張したようなインターフェース。

package errors

type T interface {
	error
	StackTraces() stackTraces // 戻り値型には議論の余地があるが、ここでは重要ではない。
}

// 唯一のT型のコンストラクタ
func WithStack(err error) T { ... }

スタックトレースを取りたいアプリケーションでは、すべての関数定義で返すエラー型を標準の errorではなく errors.Tとする。そうすると、すべてのreturnされるエラーオブジェクトからスタックトレースを取得できることが保証される。

以下の二点が嬉しい。

  1. errors.Tを返す関数しか呼ばない関数は繰り返しerrors.WithStackを呼ぶ必要がなくなる
  2. errors.WithStackをエラーが発生するたびに (deferの中ではない!) 呼び出すように保証できるので、スタックトレースがちゃんと深くなる

この方法の問題は以下。人によっては許容できるだろう(ぼくはありだと思っている)。

  1. すべての関数定義で返すエラー型をerrors.Tに統一する方法が定まっていないこと
  2. errors.WithStackをたくさん呼ばないといけないこと
  3. 独自のエラーインターフェースを定義していて気持ち悪いこと

一つ目はリンターを書けば良い。error型を返す関数を定義したら怒るだけなので簡単。deferでちゃんと書くことを保証するよりも難易度は低いはず。

二つ目は諦めるしかない。生成AIに頑張ってもらいたい。手書きするのは嫌だけど、補完があるならギリギリ許容できる気持ちがある。

三つは対処を思いつかない。これも諦めて受け入れるしかないだろう。標準のエラーとは別物として扱おうとしているのだから型は真っ当に手法を表現している。解決手法がGoぽくないのだろう。

  • deferを使う手法ではスタックトレースがちょっと足りない
    • とはいえエラーメッセージが適切に設定されていれば問題ではない
    • 込み入ったこともしないでいいし簡単
  • とはいえ設定もれが怖い気持ちや、スタックトレースをもう一段深く取りたい気持ちもある
    • そのときは今回提案した型で頑張る手法をとれば良いと思う

2025-01-04

過去に何個か一人でCLIツールを作ったことがある。そのときに雰囲気で乗り越えてしまいストレスの少ない綺麗な書き方を知りたかった概念をあげる。

  • エラーハンドリング
  • 参照周りの型変換
    • 特にパターンマッチやforループ、関数定義で & 記号を書く構文特有の意味があるときがよくわからない
  • ライフタイム
    • 実用的に我々はどんな塩梅で書くのが良いかとか、誰が所有するのが良いかを判断するためのベストプラクティスを持っていない
  • トレイトの勘所
    • ユーザ定義型に機能を実装するときに、どういうインターフェースを持たせるか迷う。どのようなトレイトを実装するかを決められない。クライアントとしての標準を知らないのでサーバとして何を提供するか、常識をもとに判断できない

要件を定めてそれを保証する仕事がソフトウェアエンジニアの仕事になるだろう。それだと単発の仕事か。進み方を把握して、それを促進する作りにする必要はあるはず。

プログラムの開発(状態や環境に反応するソフトウェアの開発)自体は生成AIによって簡単になった。要求や要件を実現することしかまだ生成AIのブラックボックスに入れられていない。要求や要件の整理の道具として生成AIを利用してはいるが、

これからは生成したプログラムに対して保証された機能の制限が大事になってくるだろう。プロダクション環境で動くプログラムを見る人間は減り、その代わりにソフトウェア検証、パーミッションの制限で安全性を保証したり、デモで振る舞いを確認する未来がくるだろう。ソフトウェアを実装するコストが低くなり、つまりソースコードに向き合う機会が減り、コードの振る舞いを静的に理解しなくなっていく。あるいは言語を高級にするだろうか。言語にどんな性質があると幸せ?

生成して良い言語に良い性質を持たせることはありそうなアプローチ。

仕事は確かにたくさん奪われそうだけど、契約をデザインして(うまくそれを表現することを含む!)プログラムを作り上げる営みは楽しいからずっと続けると思う。

2025-01-03

https://dev.nfurudono.com/posts/go-errors-stacktrace に移転しました。

大掃除は年末にしたのだけど、コーヒーグラインダーを掃除し忘れていたのでやった。結構な量の粉が中から出てきて驚いた。豆を砕く場所と粉が出てくる穴の間に傾斜した通路があってそこに溜まっていた。

今日も餃子を作った。昨日たねを作りすぎたので。ちょうど買い足した餃子の皮を消費できた。蒸しと焼きの両方やって昨日と同じ感想だった。たねを作るときに白菜の水分を抜く工程を入れていたが次回は外してみようと思う。

明日は鮭のホイル焼きをしようと思う。衛宮さんちの今日のご飯に感化された。

  • 鮭・玉ねぎ・にんじん・しめじを適切に処理する
    • 鮭には酒と塩を振って5-10min放置してから拭き取る
    • その後酒に塩胡椒を振る
  • 玉ねぎ・にんじんを敷いて、その上にコンソメをかける
  • 全てを載せつつバターも添えてホイルに包む
  • 加熱する
  • パセリか何かをかけるらしい
  • わさびマヨがあっても良いとか

朝は納豆とかかな。卵も食べよう。 朝はサンドウィッチにする。ほうれん草、卵、ベーコンを入れたい。卵しかないので買い出しが必要。パンもない。

2024-12-25

関数の引数はそれぞれ何で、どんな性質を期待するかや、そのほかの事前条件を満たした上でこの関数を呼び出すと、その結果としてどんな返り値が得られてそれ以外にどんな副作用があるかを表明するのが関数の契約。文章を用いるのが簡単だし、伝えやすくするには図を用いても良いし、型とかバリデーションのための式で表現しても良い。何にしても契約を表明すると楽しくプログラミングできる。

この関数の呼び出し側はどんなふうにこの関数を使って処理を実現するだろうかとか、この関数を実装するときはどんなふうに作ろうかと考えるのがまず楽しい。みんなが幸せになれる契約をデザインするのが楽しい。ソフトウェアのテストは契約を守っていることを検証することだと思う。型を用いた表現は契約の表明で、型検査が検証(それも厳密な)である。

このように良い契約をデザインするのがプログラミングでまずやることだと思う。その次に自動で検証するためのコードを書くのがTDDの人だし、関数型の怖い人は型で契約を表現しようとするかもしれない。そのためにすごい表現力を持つ型システムを考えて勉強して使う印象がある。

TDDで最初から完成版のテストを書かないことからわかるように、契約を最初から詳細まで書く必要はない。そんな契約を満たす実装を書けるだろうかと思うこともある。なので最初はふんわり「ユーザの情報を返すAPI」とか決めておいて、IDが欲しいとか、シャーディングしてるから作成日時も欲しい、みたいな事前要件を足したり、ユーザ情報全部返すのはきついし呼び出しがわも幸せじゃないから、名前とメルアドだけ返すことにする、みたいな調整も入るだろう。課金が滞っているユーザに対しては普通の結果は返さない、代わりにエラーを返す、みたいな変更も実装したり呼び出しているうちにしたくなるはず。

結局のところ普通のプログラミングをしようと言っているだけなのだが、僕が強調したいのは契約と実装を分けて考えようということ。契約が先にあってそれに実装を合わせる(合ってない状態はバグってる状態)考え方でいたいし、僕と契約を共有する人には(その共有する契約のレイヤで)いて欲しいと思う。

僕はmac osのコードがどうなっているかを気にすることはなくて、普通にパソコンが動けば満足するし、実際動いているので不満はない。キーボード叩いたら文字が出るという契約を守ってくれている。でもosのapiを叩く友達はそもそもドキュメントが公開されてなくて辛い、みたいなことを言っていた。このように、自分が関心のあるレイヤの契約が問題になるのだと思う。

契約はプログラミングの細かいレベルだと変数の名前とか、改行の置き方によって表明されるもので、大きいレベルだとサービスやロールの責務として表現されると思う。サービスのAPIは代表的な契約だと思う。APIという言葉は契約と同じような意味だと思うけど、エンドポイントみたいな意味でも使われているし責務みたいな概念には適用されないと思っていて使わない。

僕たちが欲しいものは何かを表明しよう、というのが契約を表明しようということだと言い換えられる。使う側からしたらその実現方法は関係ないし、提供する側からしたら契約さえ満たせば何をやっても良い。 getterとsetterを提供するからと言ってそういうフィールドをよく知られた方法で提供しないといけないわけではないし、バッファリングとか遅延実行をしても良い。

未定義な状況とか意図しない状況はある。今と昔では状況が変わって、その頃は妥当に思えた契約が時代に合わなくなることもある。契約がないと実装が良くないで話が終わってしまうが、契約があれば仮定が違ったのだと結論づけられる。人の記憶があればその人の心に契約があって仮定が違ったと判断できるだろう。記憶を失ったりその人が失われたりする場合は契約を表明しておくべきだろう。

型はデータ構造やその不変条件を表現したりそれらに名前をつけることで、契約を表現する。そして型を用いて表明された契約は普通型検査によって契約が満たされていることを検証する。正しい場合には正しいと判定するし、正しくない場合には正しくないと判定する。さらに、ある程度ちゃんとした型検査器は、どのように契約が満たされなかったかを説明してくれる。

テストは契約を検証するためのコードで、そのテストが契約のどんな部分を検証するか、テストケースの名前として表明される。その表明を満たすようにテストコードが実装されて、テストランナーに呼ばれる。契約のどんな側面をそれぞれのテストケースで検証するか、検証すると幸せか(誰にとって?)を決めるのはテスト実装者のスキルに依存するだろう。契約の中で脆いところを普通は検証するのだろう。明らかに正しそうなやつは手を抜いてしまえるはず。

バリデーションも契約を表現する手段の一つ。契約を検証処理として実装して表明するのがバリデーション。型と同じようにすべての実行について契約が満たされるかを検証する。一方でテストと同じように実際に動くものに対して検証を行う。

実際に動かして試すのは何だろう。契約とはあまり関係なさそう。実装したやつがどんな感じになってるか微妙なので試して動作を観察するのがとりあえず動かしてみる目的。これは脇道だったな。ただし、あまりによくわからなくなっているときは分割統治をするときだと思う。自分の手の中にある対象をコンポーネントに分割して、それぞれに対して契約をデザインする。その契約を満たすように実装しつつ、その契約を期待して組み合わせて元々の目的を達成すれば良い。わからない部分が新しい契約の内側にはいれば小さくなった簡単な契約を満たすことに問題は帰着できたし、それらの契約を組み合わせることに課題があるなら詳細を忘れて便利な道具を手に入れられたのだからやはり問題は簡単になっているはず。うまく契約を設計することが難しいなら、それは楽しいから問題ない。ずっと取り組んでいればいい。

何にしても契約を表明することが何より先にくる。これだけでプログラミングが楽しくなる。その後に型、テスト、バリデーションを書いたり実装をしたりする。

そういう考え方が how to design programsにデザインレシピとして書いてあるのでよかったら読んでみてください。

プログラミング言語はこの辺りが優秀で、型システムの研究とかで契約を表明して保証する方法を検討しているし実際に生かされている。なので言語に守られているうちは割と安心できる。あとは怠慢にならなければいいだけ。

プログラミング言語に閉じられなくなるとき、例えばWeb APIを提供するときは急に難易度が上がる。Web APIでも契約を表明して検証するためにスキーマ駆動開発がある。契約を書き下すことを支援するのはもちろん、型とかバリデーションで表明することも支援する。テストツールの支援もある。

僕たちが求めているのは以下である。

  • 契約を表明すること
  • 契約を洗練すること
  • 契約を犯せないようにすること
  • 契約を犯しかけたらなるはやで誰かに教えてほしい
  • これらを簡単に実現できること

最初のやつは設計しようぜ!という話。次が設計技法とか良い設計みたいな話。三つ目は型とかバリデーション、テストの話。次もそう。最後のやつはそれら全てを支援するエンジニアリングの話。

契約の話をしたのでパラダイムの話をしたい。オブジェクト指向と普通のやつの話。どっちでもいいけど、契約を表明しやすいパラダイムが良いと思う。

オブジェクト指向な考え方だと、内部状態を持つオブジェクトがメソッドを受信してその結果内部状態を変えたり返信したりする。そういうふうに契約を表明して守っていくならオブジェクト指向を使えばいい。

関数型みたいな普通のやつは、仮定が与えられた状況で関数を呼ぶと結果を出す。そういう契約の書き方をするなら関数型みたいに書けばいい。

どちらも相互に変換可能だから力まず表現しやすい言葉遣いを使えば良いだろうと思う。

割と思いの丈をダンプした。これは就職したからできたことのように思う。学生の頃に培った理想が現実世界では必ずしも簡単に実現しないことを体験できた。自明だと思っていた領域の非自明な部分に気がついて思うところが出てきた。

プログラミングの楽しいところは、ロールを分けてうまく契約群を設計することにあると思う。フレームワークを作るのはそういう契約の集まりを定義することだから楽しい。一方でフレームワークを使うと契約を設計する機会を奪われる分楽しくないかもしれない。

その分他のレイヤに注力すればいいのだろうが。

自分が自由に設計したいレイヤで他人に契約を決められるとストレスだろう。本当につまらない。反対に自分が関心なくて誰かにうまくやって欲しいときには良い契約を強い人に決めて欲しいと思う。そういうレイヤを担当するフレームワークを使って欲しいし、技術を選定したい。

アーキテクチャ設計についても言及できそう。アーキテクチャは中長期的には変遷するものだが、短期的には変わらない。アーキテクチャに乗っかってここのコンポーネントを作り込む。ここのコンポーネントをストレスなく作り込みやすくするアーキテクチャが良いアーキテクチャなのだろう。

2024-12-15

日記はマークダウンで書いていて、タイトルとか公開・非公開とかはフロントマターに書いている。それをパースしてよしなに処理してHTMLに落としたり、そもそも公開しないようにしている。それらの処理はTSで書いている。

ちょっと前にリファクタしたときにテストを書くのをサボったせいで公開・非公開の設定が反映されなくなっていた。コードベースは公開リポジトリに置いているのでクリティカルな問題ではないのだけど、望んだ挙動ではないので普通に嫌だった。

事故の原因は非公開な記事を処理対象から落とすfilterにasync関数を渡したことだった。async関数はpromiseを返すので、どんな処理を書こうがfilterは何もしないmapと同じ振る舞いをしていた、というオチ。 biomeを入れてたので弾いて欲しい気持ちがある(気が付かなかった僕が悪くはあるのだけど)。

mapにasync関数を渡して記事一覧を加工していたので、そのままのノリで放り込んだのが敗因。promise.Allした後の配列に対してfilterすることで修正した。

そういえばサイトマップとかRSSフィードとかに全く対応していなかった。自分がそもそも使わないのと、この日記を積極的に人に読ませようという気がないのが理由。じゃあいいか。ちゃんとしたブログエンジンを作りたいわけでもないし

2024-12-13

  • kamakura.goの感想とかを書く

2024-12-12

We recommend that all third-party code generators be written as plugins, as this allows all generators to provide a consistent interface and share a single parser implementation.

https://protobuf.dev/reference/other/#plugins

はえ〜って感じ。Goのライブラリはだいぶ使い勝手良かったのでまあ確かにと思ってる。

2024-12-05

viperってinit使わないといけないのかな。 viperに依存性を注入するような使い方をしたい。Facadeを受け取ってCLIを返すようなCLIジェネレータを定義しようとしていて、CLIジェネレータを実装するためにviperの諸々をinitとかで終えようと思うと困る。実行時にfacadeを入れたいので。

2024-11-28

Reactのカスタムフックの心をわかっていなかったけど、クロージャに変数をキャプチャさせて、状態をもつクロージャ(の組)を返す関数がカスタムフックぽい。組と言ったのは、getterとsetterが別々に提供されることがあるから。例えば const [v, setV] = useState()v がgetterで setV がsetterと思える。

カスタムフックの呼び出しもとの再レンダリングのたびに返却する関数の実態が同じにするかしないかは、useCallback を用いて制御できる。例えば useStateuseCallback してると思える。

このようなプログラミングテクニックを用いると、振る舞いを実現するクロージャだけが定義されていて、それが裏で扱う状態遷移がフック定義の中で隠蔽されるのが嬉しい。

カスタムフックのモチベーションは単に状態を関数型っぽいやり方で扱いたいだけなのだと理解した。

OCamlでのmutable counterの実装例を見つけた。鉄板の例だと思う。僕はこれをイメージして状態を保つクロージャと呼んでいた。

2024-11-27

Branded typeはTSでnominal typingっぽいことをするためのプログラミングテクニックの一つ。Branded Typesを導入してみる / TypeScript一人カレンダー が詳しい。

僕はこんな感じに実装に入れてみた。https://github.com/naoyafurudono/naoyafurudono.github.io/commit/778270955b4c4e7672f50600fb28dff6c12b9fa0

全てがstringになりがちで不安になるけど、これなら安心安全。型の名前を信用できるようになって、名前をつける意欲も湧く。嬉しい。

makeって、ある種CLIのDSLみたいなものだが、helpを自動生成してはくれない。いい感じにやるための工夫を見つけたのでシェアする。

以下はconnectrpcのサンプル実装リポジトリにあったmakefileの一部

.PHONY: help
help: ## Describe useful make targets
	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "%-30s %s\n", $$1, $$2}'


.PHONY: all
all: ## Build, test, and lint (default)
	$(MAKE) test
	$(MAKE) lint


.PHONY: clean
clean: ## Delete intermediate build artifacts
	@# -X only removes untracked files, -d recurses into directories, -f actually removes files/dirs
	git clean -Xdf
...

make help とすると、呼び出せるmakeのtarget一覧とその説明を出力してくれる。便利。コメントのフォーマットを定めておいて、helpを呼ぶときにはawkとかでmakefile自身を処理することでヘルプを出している。

2024-11-26

https://architecture-con.findy-tools.io/ これに来ている。

登壇資料のまとめがあるとのこと

これはあくまで僕のメモであって、発表とか僕がお話しした人がこのまま主張したわけではないです。変なところがあれば僕のせいだし、良い主張があれば彼らのおかげです。繰り返しですが僕のメモなので第三者が読んで何かに活かしたり議論の種にすることは、しない想定です。僕が便利なのでインターネットにおいておくだけ。

  • トレードオフ分析をすることが仕事
    • 最強ソリューションを出すことではない
  • トレードオフは絶対にある、ないと思ったらまだ見つかってないだけ
  • https://speakerdeck.com/findyinc/modern-trade-off-analysis?slide=15
    • デザインは挙動に関する議論であるがアーキテクチャは能力に関するものであるから、徐々に変えるのではなく最初からスパッとやらないといけない
  • フィードバックが早いのは良い。軌道修正はできる
  • ex マイクロサービスの粒度
    • 単一の責任だけでは曖昧で無理
    • 分ける要因と統合する要因を考えるべきだと主張
    • ハードパーツの本に書いてあったやつだな
  • 名前つけるのが難しいから、名前がつく粒度まで分解しちゃう、みたいな話が出てびっくりしてる。理にかなっている気もするがパッと受け入れられてない
    • メッセージングの件
  • ベストプラクティスは、状況に対して何をやれば良い、という提案。ベストなので判断の余地はない
  • 組織の目的は考える必要がある
    • 絶対壊れちゃいけない v.s. 要望からリリースまでの時間の短さ
  • 設計を繰り返すために定性分析して、結果的に定量分析しよう
  • 過去のベスプラが今野アンチパターンになるのはエコシステムがコンスタントに変わるから
  • それに追従するためにも分析を続けるのがよかろうということかな

感想: トレードオフとビジネス要求、をみて決めるのだなという気持ちと、無限に時間がかかるのでタイムボックスを決めてガッと決めることなのだろうと思った。それを何回も適宜行うイメージ。

saas: 業務のベスプラを提供するモデルテナント: 契約の提供を受ける単位のことコントロールプレーンとアプリケーションプレーンなるほど。コントロールプレーンはなるだけ作りたくないテナント、本当に必要なのか?は気になる。ここで主張されるテナントには何が含まれるのだったか。テナントの本存在するのか。

  • クライアントでブラウザを使わない、リアルタイムサーバがあるのがwebアプリとの違いっぽい
  • 専用ゲームサーバ: DGS, dedicated game serer
  • DBがやばかったのはspannerを使うことでなんとかなった

  • モブプロ

  • BDD

  • イベントソーシング

  • どんなコンポーネントがあって、どう連携するかを決める

  • vuca, ooda
  • 観察とか行動とかは具体的に何をするんだろう
  • マイケルポーター 競争優位
  • ask the speaker
    • 競合優位性でググろう、本を読もう
    • エンジニアがやっていくとスムーズそう
    • 増田さんの会社がワークショップを開催したり、本を出したりするかも

なんで全く新しく作らなかったのだろう

  • バンブーで1日かけて設計したとのことだが、どうやって決めた?意思決定プロセスが気になる。強い人?
    • 強い人が決めると決めてる。領域ごとに担当者がいる。これはknowledgeworkの意思決定プロセス(口頭でシュッと聞いただけなので、書いた内容は正確ではなさそう)
    • もちろんレビュー依頼などはする
    • それだと独裁になるのがいや。なのでこの会議で決めるとしている
    • そこまでに議題は共有されている。異論があれば準備しておく
    • 異論が出なければ決まる

scim: azureとかでユーザ作ったらsaasでもユーザ作るみたいなやつ

バッチとバックエンドはapi呼び出しで繋がってたのかな

IRでのシナリオ気になる。エンドポイントだけでなくシナリオを作ってるのかな

取り巻く環境の中で継続的な改善可能性をもつシステムを作る環境に合わせる話は大事変化を発見したときにちゃんとついていく。変化を捉えることは必要そう

  • 実現したいことをトッププライオリティにおく、ビジネスissueとしても重視する。それを実現する手段には拘らない。でもその分野についてのビギナーには手段が中心に見える。その目的が見えない。なんでその手段を取るかを理解するのは難しい
  • こうしたら良くなるの提案を頑張って説明する。自分の主張も大体間違ってると思ってドキュメントを事実ベースで論理的に書くと成功確率上がる。言語化して検証する。

  • いろんなところがconnect使ってる。layerx、knowledgework、flattが確か使っているとのことだった。
  • アーキテクチャの話を聞きにきたはずが意思決定の進め方とか社内での役割について質問していたし勉強になることが多かった。あとは共感できないか、もう知っていることが多かった

オフレコのつもりで話していた方もいるだろうからここには詳細書かない方が良さそうかな。知らない業種の話を聞けたり、一方的に存じ上げていた方のお悩みをお聞きしたのが印象的。また増田さんや他の登壇者の方に質問したり、そこから発展したお話し伺えて楽しかった。ドメインエキスパートどこにいるんだよ、いなくない…?みたいな話を質問した。他にも色々お話し聞けた。

増田さん、川中さんのお話が響いたし、発表後に質問に伺って教えていただいた知見が良かった。熱い気持ちになっている。そのほかのセッションではFlattセキュリティの内容が面白かった。ASM、そんなステップが中にあることを今まで全くイメージできてなくて、概要を知れたことが嬉しかった。 Webアプリケーションの構造を始めて学んだときみたいな気持ち。登壇者がラムダノートのWebブラウザセキュリティの本の著者だと聞いてなるほどな気持ち。

増田さんからはドメインモデリングと事業を進める話を学んだ。訳書である「ドメイン駆動設計をはじめよう」を読んで、今回の発表内容の結構な部分を腹落ちさせていたのだが、増田さんがどこをどういう形で支持しているかを知れたのが収穫。また、僕が抱えていた悩みとして「ドメインモデリングしたいが、ソフトウェアだけじゃなくてドメイン自体の設計も僕たちが結局やる必要がある。ではドメインをどう作っていくのが良いだろう」というのがあった。それを増田さんにぶつけてそれが真っ当な悩みっぽいと共感していただけた(と僕は思っている)ことと、それへの対処(競争優位を重視して作るとか、エンジニアから歩み寄ってディレクタとかと会話するとか)を引き出せたことが収穫。あと懇親会で色々お話し聞けて楽しかった。ここのお話しから収穫あったのはそうだけど、人柄を知れたのがでかい。

川中さんからはプロダクト開発における意思決定の進め方を学んだ。初期には対象範囲(バックエンドとかフロントエンドとか)を決めて強い人が決める、それに対して責任を持つ、範囲の境界の契約(APIスキーマとか)はちゃんと決めて厳格に運用する。まあ確かにシュシュっと決まりそうだし、なんとかする未来を描きやすそう。

その他の意思決定の仕方として、やはり強い人が提案をして決めにかかるやり方もあるそうだが、それだと独裁になっちゃうので最終決定を会議でするようにしてるとのこと。決める内容はあらかじめ共有しておいて、反論があれば会議までには用意する必要がある。会議で異論がなければ決まる。もちろんレビューを依頼することはある。

2024-11-04

ちょっと前からソフトウェア工学を触っている。興味から学んでいるというよりは必要に駆られて学んでいる側面が大きい。楽しい仕事をするためにつまらない状況を抑え込む力を身につけるために学んでいる。

とはいえ全くつまらないかというとそうでもなくて、ソフトウェアエンジニアやそれを取り巻く仕事はどんな体系で整理されているかを把握するのは職場の役割を整理して把握することにつながっていて面白みがある。

入門 コンピュータ科学 ITを支える技術と理論の基礎知識 (J.Glenn Brookshear)のソフトウェア工学の章を読んだ後に、ソフトウェア工学の基礎(玉井 哲雄)を読み進めている。

CSは学部の頃にある程度学んだが、その中で工学っぽい分野は触らなかったので学部レベルから勉強している。とはいえ実務経験やラボの先生のお話とかでソフトウェア工学に触れている(割とそれはそう)ので、全く素人って感じでもない。

ぼくが必要に駆られているのは要求工学なのかな。設計には純粋に興味がありそう。

2024-10-27

友達の結婚式に今度出席するので真面目に準備してみる。普段スーツを着ないので礼服を着るのはコスプレみたいでドキドキする。

  • 礼服が入るか検証
  • シャツを買う
  • ネクタイと胸につけるハンカチ買う
    • ネクタイの練習もしておかないとな
  • 靴を手入れする
  • ワックス買って練習する
  • 髪をいい感じに切ってもらうための店を見つける
    • 人に聞くのがいいかな
    • 2 or 3日に行こう(最近切ったばかりでできることあるのか?)
  • その他マナーを学んでおく

アプリケーションの開発・運用をしているとSQLのユーザをそれなりに作ることになる。データベースもしかり。それらをIaCな感じで管理したいがベストプラクティスをまだ知らない。

管理したいユーザの情報は以下の通り

  • ユーザ名(ホストを含む)
  • Grant
  • パスワード

簡単なのは、ユーザ名とパスワードだけ1Passとかで管理して、他は動いているものを見に行く方法。管理しない考え方。実態と齟齬がないことは良いことだが、設定を見るためには管理者権限が必要だしいちいち接続するのも面倒。また、複製が面倒なのも問題だし、設定の経緯をコメントに残したりバージョン管理したりできないので組織としてスケールしない。

Terraformで管理するのはいい線行きそう。ネックはパスワードかな。パスワードを平文で保持しないことを達成しようと思うと途端にややこしくなりそう。運用ではパスワードにシュッとアクセスしたいケースがあるため、他の解決策と併用するのが良いだろう。 Grantとユーザ名、変更の経緯の管理には向いていそう。

Terraformと1passを組み合わせるのが綺麗だろうと思った。ユーザ名は不変(変更したくなったら作り直す)にしておけば、色々いい感じになるんじゃないかな。

https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics が良さげ。 Refresh tokenをどこにどれくらいの期間保持するとか、どういうインターバルで無効化するとかで悩む。あとaccess tokenはauthorizationヘッダに突っ込むのが吉?とか。

ソフトウェア設計の指針をまとめた本で定番だけど日本語訳されてない(はず、2022年に買ったときはそうだった)。 APIを実装・設計したりそれに付随してコードレベルのモジュール(クラスとかで表現するようなレイヤ)を設計するときに役立つ指針をくれる。

僕がプログラミングのレビューをしたり、自分でプログラミングしたりするときに気にすることは割とここから学んだ(言語化しただけ?)と思う。インターネットでHTMLが公開されてたらどんなに嬉しいだろうと思う。レビューのときに「ここを読んでください」をできるので。

言語とかパラダイムに依存しない、プログラミング一般について使える知見が書かれているので繰り返し読む価値のあるものだと思うし、実際そうしている。以下のそれぞれのタイミングで読んだが、毎回楽しく思っている。

  1. がっつりプログラミング始める前
  2. 自分でコードを書くようになってから
  3. レビューをするようになってから

広くいろんな人に使われるAPIを設計するようになるとまた見える世界が違うのだろうと思う。

最近(日本語訳が)発売されったぽい本。内容的には流行り廃りがありそうな内容を扱っていて10年後には古くなっていそう。例えば第1章の「APIの設計・構築・仕様化」ではConnect使えばいいじゃないと思った。

とはいえこれまで10年くらいのプラクティスがおそらくいい感じに説明されていて、今どきのクラウドでいろんなサービスを動かして連携させるスタイルのアプリケーションをどうやって作っていくかを体系的に述べているように思う。ぼくはそのあたりを勉強中の身なので偉そうなことを言ったり評価をしたりできないのだけど、これまで断片的に学んで来たこと(そして腑に落としたり手を動かすに至らなかったこと)の使い所を自分の中で描けるようになっていると感じる。ほんまかいなと思うこともあるので、盲目的に信じるというよりは質の高い雑誌のような意識で読んでいる。

2024-10-26

  • aboutページの収取をするプラグインを実装して組み込んだ。あとはそれをいい感じに表示する必要がある
  • 今度人の結婚式に出るので身だしなみとか常識を身につける・髪切るとか服を用意する計画を立てる
    • 受付やるらしいのでちゃんとした方が良さそう
    • 礼服着れるか確認しておくか
  • helm完全に理解する。リリースとか意味わからん。installって何?kubectl applyとの関係は?

2024-10-10

  • protobufとかsqlcでファイル生成をするツールを使ってチーム開発をしているとすごくコンフリクトして辛い。生成するファイルを分割して、例えばrpcとかmessage単位にファイル(パッケージではない!)を分ければgitのコンフリクトは避けられる。パッケージも一緒なので振る舞いには影響しない。

2024-10-06

LLMはコンパイラとかみたいに説明とか分析を出力してくれるものではあるけど、 LLMはコンパイラよりも人間に近いものだと思っている。コンパイラみたいな決定的な出力をするツールに対して良い入力を与えてその出力をそのまま共有することには価値がたくさんあるけれど、 LLMから得られた出力をそのまま「LLMに聞いたらこう返してくれました!」っていうのは(少なくとも現代使えるLLMのレベルでは)意味が薄いと感じる。

人間に対して質問して、その回答をそのまま人に共有することにも、場合によって価値がたくさんあったり薄かったり感じることがそれぞれある。質問を回答してくれる人間に対して信頼というか専門家としての信頼というかがあったら、そのまま回答を知りたいと思うが、その辺のただの人とか単に知識がたくさんあるだけな人だと、 (アンケート回答のデータとかなら別だけど)意見や情報としては価値を見出しにくい。そんな感じに、ぼくはLLMを専門家だとはまだ思えていない。単に知識がたくさんあってそれっぽい文章を生成するだけの(論理的思考の欠けた)出力をするやつだと思う。

とはいえ知識や経験がたくさんあれば結構な量の論理的思考は不要だろうことは段々わかってきた。なんのために考え事が必要なんだろうね。そして論理的思考とは何なのだろう。

今週は疲れた。先週の金曜から毎日飲み会だとか人と会ったりだとかのイベントがあった。それぞれは楽しいのだけど全てに参加していると大変。昨日はずっと寝ていたし、今日は家事をしていた。本を読む気力もない。体としては寝ても良さそうなのだが頭は何かをしたがっているので文章を書いている。本を読みたい気持ちもあるが、落ち着いて考え事をしたい気持ちもある。今その本を本当に読みたいのだったか、とか。

わかったつもり 読解力がつかない本当の原因 (光文社新書) に本屋で出会って読んでいるのだが良い。わかるとか理解する、みたいなことは一体何なのかは高校生の頃からのぼくのテーマの一つなのだが、それを一段深めてくれている。

ぼくの中での「理解する」とは、説明をつけることとか腹落ちしていることであった。論理とかは客観的なアレとして社会的に認められていたり、自然科学は実際問題役に立っているので市民権を得ているが、そんなことは「理解している」とは関係ないと思っている。理解しているか否かは主観的な問題で、腹に落ちた感じがするなら自分はそれを理解していると思うし、自分が同意できる説明を他者がするなら他者はそれを理解しているように感じる。そういうものだと思っていたし、今も思っている。

この本の説明ではぼくの解釈と矛盾しない中で理解の程度を論じているように思う。文脈とそれに対する解釈を持っている人間は、文脈を新たに獲得し明らかにすることで対象の解釈をより鮮明にする、みたいな主張だと今のぼくは大体思っているはず。鮮明にするというか、よりわかるというか。

これまでのぼくの理解への価値観には抽象度の違いがなかった。抽象解釈をするときにどれだけfineに解釈するかの違いが認識する文脈の違いに依存することをこの本に解らさられた。

この前ご近所さんとお話した影響で今日は八百屋に行ってきた。はじめて行くタイプの八百屋で、なんとカゴとレジは存在しなくて、床に陳列されている野菜を見ながら店員さんに口頭で欲しい野菜をクエリするタイプの買い方だった。ちなみに野菜の品質はそんなに良くなかったので次は他のところかなと思っている。安くはあった。

結局人は自分と近しい考えを読んでそれを楽しいと思うのだな、みたいなことを恩師が言っていたのを思い出した。ぼくもそう思う。そういう答え合わせみたいな読書は楽しい。

読書に限らずおしゃべりとかでもなんやかんや同調とか、似たような哲学のもとでの「議論」みたいなことをするときが楽しそう。「あいつはやばいから!」と嬉しそうに友達を紹介する人は、大体その友達と大差ないとかだとか。議論をするときに背景に共通点を見出して群れるのもそういうことだと思う。良し悪しは知らない。

named-letです。

2024-09-16

  • 掃除
    • ルンバを起動したのでok
  • 外出
    • 電車乗るくらいの外出をする。秋物出たかな。あるいは外で使うと幸せになれるグッツを買うか。
    • ランニングパンツと靴下を買う。帰省でランニングのズボンを置いてきたのと、靴下が大体ボロボロになってきたので。
    • 靴下は買った。ランニングパンツはまあいっかと思って買わず。そのほかに秋に向けて服を見たけどピンと来なかったので特に買わずだった。
  • 本読む。k8sのあれ。あとは認証の技術気になるので学ぶ。認可も気になる。
  • todoをリストアップする拡張の実装
  • todoの拡張、出先でチェック入れたくなりそうなので、githubでの編集へのリンクをつけたい
    • やっぱりいいや。出先ではのんびりしよう。todoのことなんて忘れればいい。

https://diary.nfurudono.com/todos/に実装した。これでもう過去のtodoを忘れない。収集とレンダリングの両方が必要でだるかった。特にレンダリングはunifiedのアーキテクチャを理解してなくて手間取った。今の実装がunifiedのアーキテクチャに沿っているか自信がない。

本棚を作って設置したところ、そろそろキャパシティが限界だと気がついた。本棚を増やすためには引越しが必要。そろそろ減らしてもいい本も増えてきたので、 2年周期の引越しで問題ないとは思うが。今で3/4年か…、今年のうちに西側の探索をしたいな。今度友達とどこに行くか決めてないので、そっちの方に雑に行ってみて案内してもらおう。向こうが詳しいかは知らないけど。

寝たり本を読んだり景色をみたり、考え事をしたりする環境として電車で過ごすのが好き。年末は北国に電車に乗りに行きたいな。高校生の頃に一週間電車に乗りっぱなしの旅行をしたけど、あんな感じのことをまたやりたい。それでいうとシベリア鉄道は憧れる。

景色が変わるのがいいし、椅子しかないのも良い。なので新幹線みたいな机があるのはそれほど好きじゃない。横に細いサイドテーブルみたいなのはok。ものを置けるけどそこで何かやる感じではないのがいい。集中できるし、パッと休憩しようと思ったら外を見ればいい。なので電車に乗るのは日中がいい。夜は外が見えないので興醒めする。

家にいても景色が変わらなくてつまらない。外に出るのも良いが、それだと本読んだりできない。電車の中は家と同様に自分が暇なのに快適で、移動しているのがいい。

金曜日の昼過ぎに三連休だということを知った。なんと来週も三連休だということだった。この三連休は本棚を作り、あとは家でまったりした。こういうのもあり。

Goのanalysisとtypesに入門する

Goで静的解析してリンターを実装したい。具体的には、あるT1インターフェース型の変数がスコープにあるときは、T1よりゆるい任意の型の使用を禁止する、みたいな制約を入れたい。その辺に転がっている記事ではASTを覗いてみたり、SSAを覗いてみたりするようだけど、ASTとその型をまともに扱っていつつ、いい感じに静的解析ツールとして仕上げる記事を見つけられなかった。

この記事ではGoプログラムの静的解析ツールを実装するために存在する標準的なツールチェーンの思想を説明する。Go Analyzerは静的解析ツールフレームワークとして用いる。具体的な静的解析には標準ライブラリを用いる。

具体的には、あるT1インターフェース型の変数がスコープにあるときは、T1よりゆるい任意の型の使用を禁止する、みたいな制約を入れたい。

これを検証するGoの静的解析ツール。例えば以下のようなrの使用を検知したい。

func parse(r io.Reader) (string, error) { ... }

func (rw io.ReadWriter) {
	var r io.Reader = getReader()
	s, err := dump(r) // 「rじゃなくてrwを使ってくれ〜!」と指摘したい。
	...
}

https://pkg.go.dev/golang.org/x/tools/go/analysis これ。準標準なパッケージ。ドキュメントの冒頭に思想がちゃんと書かれているのでそれを読むのが良さそう。

一個の解析ツールに対応する。Analyzer.Runに解析を実行する関数を定義する。この解析ツールを実行するとき(実行するのはフレームワークの仕事)に、Pass構造体が渡される。

Pass構造体は解析対象のパッケージごとに作成される。なので解析の単位はパッケージごとということになりそう。いいじゃん。

type Pass struct {
	Fset         *token.FileSet
	Files        []*ast.File
	OtherFiles   []string
	IgnoredFiles []string
	Pkg          *types.Package
	TypesInfo    *types.Info
	ResultOf     map[*Analyzer]interface{}
	Report       func(Diagnostic)
	...
}

こいつを通じて解析をする関数は処理対象のデータにアクセスしたり、処理結果を報告したりするぽい。モナドとか代数的エフェクトみたいで綺麗だ。そう思うとあれらは抽象化された一つの振る舞いの切り口を表現するための基本的な演算を定義していたのだから、まあそうだなと思える。Kokaで静的解析ツールを作るときにはpassエフェクトを定義するのだろう。

The Fset, Files, Pkg, and TypesInfo fields provide the syntax trees, type information, and source positions for a single package of Go code.

これは本質情報の予感。このあたりにうまくアクセスすることで、ぼくたちの頭の中で想像する型付き抽象構文木へのアクセスを実現できるんだろう。データ構造が思ったのと違いそうなことには気をつけよう。

これらに加えて、他のanalyzerが出力してくれる結果をこのanalyzerの入力として使える。それにアクセスするためには pass.ResultOf[a].(aResType)を参照すればよい。

診断(diagnostics)を出したければPass.Reportとか、パッケージで提供されているReportfとかを使うらしい。

ここまでで、モジュラーに静的解析ツールを実装するフレームワークの構造がわかった。それに乗っかれば静的解析をいい感じに動かすことはできそうだ。静的解析の処理を実装する方法もなんとなくわかった。Pass構造体の世界観に乗っかればokな感じがする。

次に自在にプログラムを解釈する方法を知りたい。プログラムはコンパイラに処理されていろんな形態に変換されるので、ユースケースに応じて適切な表現を選ぶ必要がある。今回は型付き抽象構文木を扱いたいので、Pass構造体のFset, Files, Pkg, TypesInfoあたりを上手に使えると良さそうだ。特にTypesInfoが気になる。これは types.Info型をとるみたいなので、typesパッケージを見に行く。

typesパッケージはこれ https://pkg.go.dev/go/types。冒頭の説明がスッキリしていてまだ何をやれば型付き抽象構文木に対してクエリっぽいことをできるか、どんなクエリっぽいことが許されるかを理解できない。なので貼ってあったチュートリアルのリンクを辿る。なお、スッキリしている説明自体は読んでよかった。このパッケージが扱うフェーズで何をやるか説明されていて、もっと詳しく読んで良さそうなことに自信を持てた。

なお、僕の目的のためには他の解析ツールの結果を使う方が良いかもしれないとも思う。暇だし気になるのでチュートリアルを読むのに時間をかけるけど。

脱線したがチュートリアルを読み進める。https://go.dev/s/types-tutorial これ。これを読む目的は、何をやれば型付き抽象構文木に対してクエリっぽいことをできるか、どんなクエリっぽいことが許されるかを理解すること。

このチュートリアルはジェネリクスには対応してないらしい。ジェネリクスのためのドキュメントは別途あるとのことだけど、今回は基礎を知りたいので気にしない。

イントロと例くらいは読んでみて、あとは斜め読みでいいかな。まずはイントロ。

Measured by lines of code and by API surface area, it is one of the most complex packages in Go’s standard library, and using it requires a firm grasp of the structure of Go programs.

とのこと。大変だ。

Starting at the bottom, the go/token package defines the lexical tokens of Go. The go/scanner package tokenizes an input stream and records file position information for use in diagnostics or for file surgery in a refactoring tool. The go/ast package defines the data types of the abstract syntax tree (AST). The go/parser package provides a robust recursive-descent parser that constructs the AST. And go/constant provides representations and arithmetic operations for the values of compile-time constant expressions, as we’ll see in Constants.

データ構造とアルゴリズムを分けるの賢そう。parserにastを定義しないとか偉い感じがする。色々あるんだろうな。どう嬉しいのかはわからないけど。定数畳み込みをastに対して実装したいが、parserに依存するわけではないよね、みたいな話かな。

名前解決、型検査、定数式の計算は一緒にやらないといけないなるほど。ここでいう名前解決とは、名前の出現に対してその宣言を対応させること。

例まで読んだがパッケージレベルの話しかわからないな。ぼくは式とかのレベルでプログラムを処理したいんだ!ということで本命のInfo構造体への言及を探すことにすると、TypeAndValueでそれらしいことを述べている。

Info.Typesは map[ast.Expr]TypeAndValue らしい。そろそろ手を動かして、プログラムのこの要素は式として扱われるか?とかをみたい。と思ったらドキュメントが例を出してくれた。こういうときが一番楽しい。式があったら型は得られるようになってるのね。ただまだよくわかってなくて、mapの定義域をast.Exprとしているが、そのExprとして本当に登録されるのはどの範囲のExprなのかがわからない。当然 ast.Expr{} なんて渡しても、その型を計算しているわけがない。どういう操作で手に入れたast.Exprに対しては、Info.Typeがその型を教えてくれるんだろうか。Infoを生成するやつが知ってるのかな。analysisパッケージはよくわからんPassがInfoを持っていたので微妙だけど、パッケージの単位で処理をするのでパッケージに存在するすべての式の型を教えてくれると思って良さそう?

確かにConfig.Checkはパッケージを型検査して、引数にInfoへのポインタをとって結果を書き込みそう。

次に、型同士の比較をしたい。具体的には、T1が必要な文脈でT2は使えるか (assignable) を判定する方法が欲しい。そのためにこれが使えるhttps://pkg.go.dev/go/types#AssignableTo。引数に渡すTypeインターフェースの値はTypeAndValueで取れるので、ほとんど勝ったようなもの。ちなみに僕は、最初ConvertibleToを使っていて全然ダメだった。ConvertibleToは数値が変換できるか判定するやつぽい。

2024-08-15

日記をnextjsでビルドして、cloudflare pagesにデプロイするようにした。cnameの設定が効くようになれば、https://diary.nfurudono.comから見えるようになるはず。

cloudflareのアカウントを初めて作って設定したが、体験よかった。デプロイとか周りの開発のためにCLIツールやそれのgithub actionsバインディングが提供されていて、サクッとデプロイできた。記念すべき初デプロイの成功をメモしておくhttps://github.com/naoyafurudono/naoyafurudono.github.io/actions/runs/10399555684/job/28798614059

Hugoの設定をたくさん書いていても楽しくないのでNext.jsでなんでもやってみよう、というモチベーション。unifiedがいい感じで楽しいので動くところまで持って来れた。

ソースコードはhttps://github.com/naoyafurudono/naoyafurudono.github.io/tree/main/tools/ttにある。

2024-08-03

ソフトウェアを設計するときにはどこを変えやすくしたいか(どんな変更をしやすくしたいか)を把握したい。 Expression probremは必ずしも解決しなくてよいと思っていて、変更に必要な気合がどれくらいかを把握したうえでそれを許容できるかが論点だと思う。

もちろん使う気合が小さいのに越したことはないのだが、コード読み書きするのはみんな好きだろうし、嫌いなひとは割とAIに任せられる世の中なのだから、小手先のテクニックでなんでもできる設計とかにしておくうまみはあまりないと思う。

会社ではmacbook proを使っていて、家ではmacbook airを使っている。使っているソフトウェアとか設定は大体同じにできていて便利ではあるのだけど、今日は手ぐせで会社でよく触っているリポジトリにcdしようとして失敗するのを何回もやってしまった。 lsのノリでそのディレクトリにcdすることに辛さを感じる。

  • 飲み会の場所を抑える
    • ビールをたくさん飲めるところでそう遠くないところがいい
  • 健康料理
    • 野菜を食べたいです
  • 外出
    • 15:30を回ったが、まだ外に出ていない。お日様が欲しい
  • 洗濯
    • あとは干すだけ
  • 運動
  • ルータを吊るす
    • そういえば家の壁に釘打っても怒られなさそうな板が壁に付いているので、そこにルータをなんとかつけたい

今日は小学校4年生の頃に転校してきた友達の誕生日。ブルーアイズホワイトドラゴンをくれた、寡黙なやつだった。元気だといいな。

AHK入門

Windowsマシンでキーボード操作が不便なので導入する。悪態をつきながらも使っている人を知っているので内容を理解していないが期待している。

https://www.autohotkey.com/ 公式ページからバイナリを落とせる。インストールするとwelcome画面が開いたのだが、そこに「コンパイルする」ボタンがあって、ちょっと不安になる。僕はプログラムをボタンを押してコンパイルしないといけない?

ついてきたマニュアルはいい感じのスタイリングでよみやすそう?

頭から飽きるまで読んでいく。

  • スクリプトの作成 (Create a Script)
    • Be sure to save the file as UTF-8 with BOM if it will contain non-ASCII characters. For details, see the FAQ.

    • BOMがいるらしい。そういえばBOMってなんなのだろう。結局まだちゃんと理解してない。
  • スクリプトの実行 (Run a Script)
    • なんかソースファイルをダブルクリックしたりして実行できて、実行してる間だけ効くらしい。
    • イベントハンドラみたいなやつの定義がそれぞれのスクリプトに対応するかと思っていたが、なんかメンタルモデルがあってなさそう
  • それぞれのスクリプト実行がWindowsのトレイアイコンに反映されるらしい。なるほど

飽きたのでhello worldする。

以下はCapsLockを押すとhello, worldと出力するよう設定するahkスクリプト。ちなみにこれらのhello, worldはCapsLockを打って入力している。

#Requires AutoHotkey >=v2.0.0

CapsLock::
  {
    SendInput "hello, world"
  }
  Return

CapsLockで英数変換するのは以下で実現できた。

#Requires AutoHotkey >=v2.0.0

CapsLock::
  {
    SendInput "^{Space}"
  }
  Return

紆余曲折あって、以下のようになった。Windowsでもいい感じにvim使えるようになって幸せ。

https://github.com/naoyafurudono/dotfiles/blob/main/autohotkey.ahk

#Requires AutoHotkey >=v2.0.0

IME_SET(SetSts, WinTitle:="A")    {
    hwnd := WinExist(WinTitle)
    if  (WinActive(WinTitle))   {
        ptrSize := !A_PtrSize ? 4 : A_PtrSize
        cbSize := 4+4+(PtrSize*6)+16
        stGTI := Buffer(cbSize,0)
        NumPut("Uint", cbSize, stGTI.Ptr,0)   ;   DWORD   cbSize;
        hwnd := DllCall("GetGUIThreadInfo", "Uint",0, "Uint",stGTI.Ptr)
                 ? NumGet(stGTI.Ptr,8+PtrSize,"Uint") : hwnd
    }
    return DllCall("SendMessage"
          , "UInt", DllCall("imm32\ImmGetDefaultIMEWnd", "Uint",hwnd)
          , "UInt", 0x0283  ;Message : WM_IME_CONTROL
          ,  "Int", 0x006   ;wParam  : IMC_SETOPENSTATUS
          ,  "Int", SetSts) ;lParam  : 0 or 1
}

IME_GET(WinTitle:="A")  {
    hwnd := WinExist(WinTitle)
    if  (WinActive(WinTitle))   {
        ptrSize := !A_PtrSize ? 4 : A_PtrSize
        cbSize := 4+4+(PtrSize*6)+16
        stGTI := Buffer(cbSize,0)
        NumPut("DWORD", cbSize, stGTI.Ptr,0)   ;   DWORD   cbSize;
        hwnd := DllCall("GetGUIThreadInfo", "Uint",0, "Uint", stGTI.Ptr)
                 ? NumGet(stGTI.Ptr,8+PtrSize,"Uint") : hwnd
    }
    return DllCall("SendMessage"
          , "UInt", DllCall("imm32\ImmGetDefaultIMEWnd", "Uint",hwnd)
          , "UInt", 0x0283  ;Message : WM_IME_CONTROL
          ,  "Int", 0x0005  ;wParam  : IMC_GETOPENSTATUS
          ,  "Int", 0)      ;lParam  : 0
}

IME_TOGGLE() {
  current := IME_GET()
  IME_SET(!current)
}

IME_OFF() {
  IME_SET(0)
}

CapsLock::
  {
    IME_TOGGLE()
  }

~Esc::
  {
    IME_OFF()
  }

認可に入門 wip

https://www.osohq.com/academy これを読んで認可に入門します。頑張るぞ〜!

https://www.osohq.com/academy/authorization-academy

Chapter II: What authorization is: how to organize your authorization code Chapter III: Role-based access control: grouping permissions in your app into roles like User and Admin Chapter IV: Relationship-based control: grouping permissions by relationship, like “the creator of a post may edit that post” Chapter V: Authorization enforcement: once your app has decided to allow or deny access, what do you do with that decision?

こんな感じとのこと。色々あるんだな。どれも気になるが、特にアプリケーションコードにどうやって統合していくかが気になるところかな。もれなく頑張らずに実現したい。

https://www.osohq.com/academy/what-is-authorization

具体例を挙げてWebアプリケーションにはレイヤがあって、それぞれのレイヤではどんな情報を取れて、どんな認可ができるかを議論する。

また、“4. Adding Authorization to an Application>”ではコードでの認可処理の書き方を比較する。

Naiveなアプローチだと以下のつらみがあると言っている。それな〜。

That quickly gets difficult. As the number of places where you need to apply authorization increases, you end up duplicating the same logic. Making any change then requires us to remember every place our logic is duplicated.

どうしたらいいんだ…

Formalizing Our Authorization Model なるほどね(以下を認識する)。

  • actor
  • action
  • resource

UNIXのファイルにつけるパーミッションもこの考えで整理されてそう。どのユーザがどのファイルに対してどんな操作をするかを気にして権限の管理をしてるイメージ。

グループIDみたいなのはactorを発展させたものぽい。 sudoもその辺をいじるのかな。Capabilityはactionsの部分かな。それだけでもなさそうか。

上の三つ組を考えるメリットは二つあるとのこと。

  • 言語を共有できる
  • 設計としてシンプル

設計がシンプルと言ってるのは、以下のように認可のインターフェイスを持てそうということらしい。

isAllowed:: (Actor, Action, Resource) -> Bool

こういうインターフェースを定めることで認可の周りで責務の境界を定められる。呼び出し側の責務にenforcement, 呼ばれる側の責務にdecisionという名前をここではつけている。

認可処理の結果をもとに何をするかを決めること。そもそも認可処理の呼び出しをするのもここの範囲だし、その結果アクセスがなければ403返すとかするのもここ。良いかどうかを気にしつつ、その結果どうするかを決めるのがここ。

基本はyes/noを返すやつ。さっきの関数の実装。警告とかは出したきゃ出せばいい。純粋である必要はなさそう。

5. Putting Everything Together がまとめだった。ここまでのメモが集約されてる感じ。

あとここには書かなかったけど、enforcementとかdecisionとかの話の後にenforcementをどこに実装する?みたいな話があるがマイクロサービスぽいアプリケーションを書いてないとそもそも問題にならなさそうで、一旦そこに僕は興味ないのでスルー。

アーキテクチャの話はなるほどって感じだが、まだ考えられることはたくさんある。

  • 権限が足りないときのエラーメッセージの返し方
    • 足りない場合は403だけじゃなくて何を足せば良いか知りたいとか
  • 管理者にアプリユーザがどんな権限を持ってるかを表示するとか
  • グループとか

https://www.osohq.com/academy/role-based-access-control-rbac

半開閉区間

コンピュータで区間処理するときは半開閉区間で持つことが多い気がしている。もはや信仰のレベルで身についているので自分一人で決めて終わりなときはそれで良いのだが、説明が必要なときに困る。そのときのために良さげな参照を集めておく。

何というか区間は連続な集合の2端点を指定して定まる部分集合であって、その全体からなる集合族を扱おうと思うと半開閉区間にしておくと綺麗というのも関係する?

区間は全順序集合の部分集合で、それらを体系的に考えようと思うと区間の集合の性質を考えるのが良いはず。つまり区間からなる族を考えると良さそう。

考える全順序集合が連続な集合なら、半開閉区間からなる族を考えると、任意の区間は有限個の区間の非交和として表現できる半開では無い区間の族ではこの性質は成り立たなさそう。漏れとダブりがないように区間を分割する必要は実用的に割とあってそれをきれいにできるのは半開閉区間だけということになる。

ググってみると集合半環という概念があるみたいで、それにこの性質が効いてるぽい。集合半環 (wikipedia)をみると良さそう。

むずい。

https://chatgpt.com/share/4c778bf6-f43a-4230-825d-398ad025a457

結構良い回答をもらえた。

プログラミングで区間を表現する際に半開閉区間(半開区間、片開区間とも言う)を用いると良い理由は、特に次のような点にあります:

  1. 直感的なインデックス操作:

    • 配列やリストなどのデータ構造において、インデックスは通常0から始まります。半開閉区間 [start, end) を使うと、start から end-1 までのインデックスが含まれるため、範囲を扱う際に計算がシンプルになります。例えば、要素数が10の配列に対して [0, 10) という範囲を指定すると、全ての要素を含むことになります。
  2. 区間の連結が容易:

    • 半開閉区間は連結する際に便利です。例えば、 [start1, end1)[start2, end2) という2つの区間を連結する場合、end1start2 が同じであれば、それらを連結して [start1, end2) とすることができます。このように、区間の端点が明確に分かれているため、エッジケースが少なくなります。
  3. 境界の一貫性:

    • 境界条件を処理する際、開区間の端点は含まれないため、範囲外アクセスのリスクが減少します。これにより、特にループ処理や再帰的なアルゴリズムの実装が一貫しやすくなります。
  4. 数学的な整合性:

    • 半開閉区間の表現は、数学的にも広く使われており、理論的な背景からも自然な表現です。例えば、集合の分割や区間の分割などで、一貫した扱いが可能です。
  5. プログラミング言語のサポート:

    • 多くのプログラミング言語やライブラリが、半開閉区間をデフォルトでサポートしています。例えば、Pythonのリストスライスや、C++の標準ライブラリの範囲は、半開閉区間の形式を採用しています。このため、言語やライブラリの機能を活用する上で半開閉区間を使うのが自然です。

以上の理由から、プログラミングにおいて区間を表現する際には半開閉区間を用いることが一般的であり、推奨されます。

数学的な観点から見て、半開閉区間(例えば ([a, b)))を使用することにはいくつかの重要な性質があります。これらの性質により、一貫した扱いや直感的な操作が可能となります。以下にいくつかの重要な性質を挙げます:

  1. 区間の連結性:

    • 2つの半開閉区間 ([a, b)) と ([b, c)) を連結すると、新しい半開閉区間 ([a, c)) を得ます。これにより、連続する区間を簡単に扱うことができます。
    • 例: ([1, 3)) と ([3, 5)) の連結は ([1, 5)) となります。
  2. 区間の分割性:

    • 半開閉区間 ([a, c)) を点 (b) で分割すると、2つの区間 ([a, b)) と ([b, c)) になります。このように、区間を分割する際の扱いがシンプルです。
    • 例: ([2, 7)) を (5) で分割すると ([2, 5)) と ([5, 7)) になります。
  3. 一意性と包含関係:

    • 各点は1つの半開閉区間に一意的に属します。具体的には、点 (x) が区間 ([a, b)) に属するのは (a \leq x < b) の場合のみです。このため、区間の包含関係を判断するのが容易です。
    • 例: (x = 3) は ([1, 5)) に含まれますが、([3, 7)) にも含まれます。
  4. 空集合の扱い:

    • 半開閉区間において、区間 ([a, a)) は空集合となります。これにより、区間のサイズが負になることを避け、理論的な一貫性を保ちます。
    • 例: ([4, 4)) は空集合として扱います。
  5. 順序性と計算の簡便性:

    • 半開閉区間は順序を持っているため、数直線上の位置関係を直感的に理解できます。これにより、範囲の計算や比較が簡単になります。
    • 例: ([2, 5)) と ([5, 8)) の関係は明確に非重複であると分かります。
  6. 測度論との整合性:

    • 測度論(特にルベーグ測度)においても、半開閉区間は重要です。半開閉区間は測度が容易であり、特に区間の長さが (b - a) で計算できるため、実際の応用において便利です。

これらの性質により、半開閉区間は数学的に一貫した扱いが可能となり、プログラミングにおいても同様の利便性が得られます。このため、アルゴリズムやデータ構造の設計において半開閉区間を使用することが推奨されます。

閉じたとこから開いたと〜こ!

好き好き大好き〜

RubyKaigi2024参加メモ

RubyKaigi2024に参加したので、思ったことをメモしておきます。

参加したセッションはこちら: https://rubykaigi.smarthr.co.jp/2024/plans/d2350276-c631-4bdc-ad75-49e446e798a3

今回のセッションをいくつか聞いてShopifyのやり方に憧れるようになった。エンジニアリングをしていく上での姿勢として、課題に対して上流から対処しよう、みたいな箴言があってそれが心に残っている。 ShopifyのRuby周りのチームはまさにそれを地で行っていると今回のセッションを聞いて感じたそういうチームに所属して(作って?)良いエンジニアリングをしていきたいと思っていたのだが、これまでは具体的なイメージいを持っていなかった。

ShopifyのRubyチームによる貢献にはすでに自分が直接的に恩恵を受けているし、彼らがどういう思想で取り組んで具体的に何をしてきたか、これからどういう思想でやっていくかを生で聞くことができた。遠いけれども具体的に目標とする存在に出会えたことが今回の一番の収穫だったと思う。

バージョンとかライブラリのインストールとか大変だしよく分からないのでスッキリする方向に進んでいきそうで楽しみ。セッションも普通に勉強になった。

一方的に知っていた人もお互い初めましてな人もお話しできてよかった。今回存在を新たに認知した人ももちろんいて、いろんな人が色々やっていることとか、意外とコミッタ少ないこととか認知できて良い。 Rubyって人間が作ってるんだなと感じる。

現実味を感じる一週間でした。

ElmアーキテクチャをReactで実現する話

リアクティブプログラミングの勉強をしていた頃にElm言語を知って好きになった(全然コード書いてないけど)。特徴はElmアーキテクチャにあると思っている。 Webアプリケーションの大体のフロントエンドアプリケーションはこう言うアーキテクチャで表現できるはず、みたいなことを言っていて、そのアーキテクチャが簡単なので気に入ったのだろう。

ユーザやAPIとやりとりをするアプリケーションなので、外界からの入力は当然ある。

また、それによって(のみ)アプリケーションの状態は変化する。アプリケーションが取りうる状態はアプリケーションに固有だし、どんなことがアプリケーションで起こって(入力としておきえて)、その結果どのような状態遷移をするかもアプリケーションに固有。なのでそれらはアプリケーションの実装者が決める。入力というかイベントがあって、それによってある状態から他の状態に遷移する、状態遷移系を定義するのはシンプルに思える。

状態が決まればそれの描画結果も決まるはず。もちろんDOMとかスタイルを当てたりとかして状態を描画結果に対応づける方法はそのアプリを作る人が定義する。描画というのは状態から描画結果への写像なのだ、と思うのがシンプルなポイント。

最初にあげた、外界からの入力は曲者。クリックとかレンダリングとかAPIの呼び出しとかは、知ったことではない感じがする。少なくともフロントエンドアプリケーションの範囲で意味を定義して実装するものではない。なのでその辺はElmのランタイムがいい感じに実装してインターフェイスを提供してくれる。そのインターフェイスを使いつつ、イベントを定義することになる。

Elmアーキテクチャは最高なんだけど、仕事のプロジェクトで使うとなると言語の選択はもっと資本主義ぽい判断が必要になって、TypeScriptとReact使うかという気持ちになる。

幸いなことにこれら二つでElmっぽいことはできるので、今の仕事では黙々とそれを進めている。 Reactで違うところは、Elmランタイムがなくてもうちょっと小さな部品がたくさんあって、それをいい感じに組み合わせて使うところだと思う。useEffect で頑張るとか。

そのあたりを頑張っているうちとかに、だんだんElmアーキテクチャっぽくするのが辛くなったりして辛い。みんなで強い気持ちでその方針に向かうのが良さそう。

https://guide.elm-lang.org/ がElmのガイドでElmアーキテクチャを推してくれる。なのでみんな読んでほしい。

エフェクトハンドラで実現したい世界と、Elmアーキテクチャみたいな世界は重なる部分が多そうだと感じている。 Reactの開発者もuseContextはalgebraic effectなんだ!みたいなことを何処かで言っていた気もするし、誰しも思うことなんだろう。

2024-03-26

イベントソーシング?と言うのかわからないけど、イベントを全部持っておけば現時点での状態はイベントを全部みて計算できるので永続化しなくて良くない?みたいな考え方がある(はず)。

イベントを解釈する遷移規則が変わらなければそうだけど、実際のソフトウェアではプログラムは継続的に変わっていくもので、イベントの解釈が変わらないこと(もっと言えば解釈できること)を保つのは大変そう。検証するためにリグレッションテストみたいにスナップショットを取っておいて、遷移規則が変わるたびに新しい解釈結果と比較すれば良いかもしれないw。

世間ではその辺どうしているんだろう。遷移規則が変わらない範囲でこのアーキテクチャを採用することでクリアしてるのか、そんなことは気にしないのか(あるいは気にならないのか)、他に上手いやり方があるのか。

このアーキテクチャを採用する目的にもよるか。

Nextjsのプロジェクトにstorybookを導入してみた

https://github.com/naoyafurudono/timer/pull/1

このプルリクエストで頑張った。コミットメッセージにやったことは書いてある。

公式ドキュメントがしっかりしてそう。storybookが内部で使っているwebpackが@ インポートを読めないみたいで、プラグインを入れる必要があった。

2024-03-20

Webアプリケーションのテストをどう実施したものかと最近考えることが多い。 Webアプリは雰囲気で変更したいはずで(ソフトウェアって大体そう?)、またテストは長持ちしてほしいと思う。

テストではなんらかの仮定のもとで実装がある条件を満たすことを検証する。その仮定を上手に置くことがテスト戦略を決める人の腕の見せどころだろう。

例えば関数の単体テストをする場合は、そこに渡す実引数を仮定して、その関数の出力が意図した通りになることを検証するし、モックを使うならそのモック対象の振る舞いを仮定することになる。 e2eではユーザの操作を仮定するのだろう。

いろんな仮定の置き方とその結果実現できる検証内容がある気がしていて、安定した仮定(覆るとテストを直さないといけない)を選びつつ、そこがクリアできれば安心と思える検証内容を取ることがテスト戦略の設計で目指すことなのだと朝の東横線で思ったのだった。

で、そういうお得さを出せるように実装するのも大事なんだろうなと思っています。

2024-03-09

フロントエンドのモデルとRPCについて考えてみたい。

Webアプリケーションのフロントエンドがリッチなときに、テストをどんな感じで書こうかとか、そもそもアプリケーションをどんな感じで書こうかと悩む。僕は悩んでいる。

ユーザとページがあって、必要に応じてAPIを呼ぶ、その結果画面遷移が起きるくらいのものだったのが、しっちゃかめっちゃかしてきてそれを理解できるのはコードを書いた時空にいる人のみになる。

フロントエンド、副作用が多すぎるんだよな。レンダリング、人間、apiが絶対にいるのでそれは大変に決まっている。ログを気軽に残せないとか、エラーの出し方に気を使うのも辛い。Webアプリのバックエンドなら、しょうがないからログたくさん出して500返しとくかができるけど、フロントエンドで想定外が起きたときに、すまん壊れただけでは完結しない。

そもそも完結しないようなレイヤ分けが間違ってるのか?ネットワークの隔たりとレイヤ分割が一致してしまっているが、フロントエンドアプリケーションも気軽に壊れさせてあげてほしい。その内容をログに取ったりユーザに幸せになってもらうためのあれこれは他のレイヤでやるべきなんじゃないかと思ったりしました。

今お酒を飲んでいるし、今日会社で上司というか先輩というかがウイスキー好きだとしって酒モチベが高い。最悪だな。

お酒を楽しく飲める会をしたいね。酒の好きなところを語ると良さそうに感じたのでやります。

まずいやつはまずいけど、美味しいやつは本当に美味しい。他の食べ物と同じで体調とか好みによるのだけど、摂取するもので酒が一番美味しいことはままある。ウイスキーとビールは特にそう感じる。日本酒とかワインはそんなに美味しくないと思います。

アルコールを摂取するといわゆる常識を司る理性が外れて、あらゆる人と小学校の友達のように接することができるようになって楽しい。それっぽい言い方をした(?)けど、僕が小学生みたいな動きをしてるだけかも。

あと音楽聴くときにはアルコール入ってるともっと楽しめて好き。

インカレの1,2週間前くらいな感じがして楽しい。フルタイムで専門目な領域なのが痺れる。

2024-02-17

glangにgeneratorを入れようというプロポーザルがあって、試験的に実装されているみたい。いくつかのパターンがあるけど、だいたいこんな感じで使える (The Go Playground)

ジェネレータとして使われる関数は定義の段階では普通の関数と区別がつかないような構文定義をされている。コンパイラやランタイムの実装が気になるし、静的解析ツールが大変なことにならないかが心配。

気が向いたらプロポーザルを読もう。ジェネレータが引数にとる関数がブールを返すけど、どちらを返すかを誰が決めているかも気になる。

第一印象ではgolangには入らないで欲しいなと感じた。

2024-02-13

今日1on1で教えてもらった。ロールモデルとして追っていく。https://scrapbox.io/kawasima/%E3%82%A4%E3%83%9F%E3%83%A5%E3%83%BC%E3%82%BF%E3%83%96%E3%83%AB%E3%83%87%E3%83%BC%E3%82%BF%E3%83%A2%E3%83%87%E3%83%AB

2024-02-12

管理画面の設計について考えてみる。 Webアプリケーションでもろもろを設定する際のUI設計を考えている。

設定の初期化時や更新時でフローが異なるはずだ。

初期化のタイミングでは、氏名やメールアドレスの設定・認証や、オプショナルな事項(決済情報とか追加の契約事項とか)の設定が挟まり、最後に確認してよければコミット、ダメなら修正する。

更新では最初の設定で気に食わなかったところを修正したり、追加の設定項目をいじったりするだろう。

それぞれのタイミングで何を設定させるかや、どれだけサクサク入力できるかによってユーザの体験が左右されそう。例えば初期では認証情報だけをコミットして、その他の情報は更新で登録する手もあれば、ファストバスを登録時に持ってきてしまって、8割のユーザが最短で使用を開始できるようにするパターンもあるだろう。

ユーザが行うそれぞれのフローはアプリケーションというかシステムにどのような影響を与えるだろうか。というよりも、ユーザがそれぞれのフローの結果及ぼせる作用をどのようにデザインすると良いだろうか。

この作用をデザインするにあたって、アプリケーションで表現するモデルがどんなであるかを明らかにしておくと良いはずで、そのモデルに対する作用の一つの実現方法としてユーザが操作するフローがある、と思えそう。

ユーザによる操作じゃない作用としては、決済が行われたとか権限が時間経過で失効したみたいな作用がありそう。

データにもいろいろありそう。モデルを表現するためのデータと、作用を実現するために一時的に保持するデータは異なる。

例えばユーザが登録されているか登録されていないかしかないモデルでは、メール認証中にはユーザが登録されていないけど、メール認証のためのトークンとかはサーバにデータとして持っていないといけない。

もちろん、そういう一時的な状態もモデルに含めるてもあるはず。RDBで実装する場合delete操作が激しくなりそうだけど。セッションキーもそうかな。セッションキーはモデルを表すデータに持つことは少なさそう。モデルの表現には含まれないログイン状態として扱われるイメージがある。揮発性のあるデータストアで保持しそう。

全部モデル化してしまうのが教科書的なんだろうな。汚いとわかるならあえて汚いことをやる必要はないだろう。

モデルではないけど、DBMSの都合について説明する記事があったので貼っとくセッションの保存などにRDBMSよりもRedisやmemcachedのような、NoSQLが向いているのは何故ですか?他にNoSQLの用途として向いている(いない)処理にはどのようなものがありますか? (Quora)

TTLが欲しいよね、という感じか。確かにそもそもセッションキーにはTTLが欲しい気がするし、モデルを考える上でTTLはあまり意識したくない気もする(頭弾けそう)。

反面、TTLもうまく表現したら面白そうな気もするし、割と欠かせないんじゃないかという気もする。そのときに普段のモデルを表現するのに使うDBとTTLを複雑さを吸い取らせるために導入したDBが異なると不必要な複雑さが生じたりするかもなと思った。

今日たくさん寝て軽くランニングしたらなかなか幸せな時間を過ごせた。ただいまはそのクオリティが持続していない。夕食の目処が立っていないこととか部屋がちょっと寒いこと、姿勢が微妙なことが原因だろう。軽く座って作業する空間はやはり欲しい。机にがっつり向かうか後傾でノートパソコンを叩くか、床で作業するかの三択でQOLっぽい作業時間を過ごす空間がない。コメダで作業する姿勢を家でも取れるようにしたいイメージ。やはりベンチソファだと思うんだよな。僕のユースケースだとソファだけでなく机も欲しいことに気がついたのが今週末の成果。

あとは壁が寂しいことにも気がついた。なんか怖くないすっきりとした絵とか飾ればいいんだろうな。めんどいので誰かに決めてほしい。ああいうのってどこで売ってるんでしょうね。

推し言語機能 Racket編

この記事はGMOペパボエンジニア Advent Calendar 2023 🎅会場の19日の記事です!

昨日はyagijinさんのReactやってる人向けのSwiftUI入門でした。 Swiftに興味があるReact信者の僕のために書いてくれたのかと錯覚しました。これを期にSwift UI入門しようと思います。 Swift UIは双方向バインディングを採用しているとのことなので、Vueとの類似もありそうですね。

Reactを書いている時間は癒しの時間です。ところでReactは関数型言語からインスパイアされた機能が多いですよね。今日の記事はそんな関数型言語の中でも僕の好きなRacket言語の記事です。僕が一推しするRacketの言語機能を紹介します。

好きなRacket言語の言語機能を紹介します。ざっくりとした紹介なので、ここでの知識を人に話したりプログラミングで活用する前に、節々で参照する公式ドキュメントを参照してもらえると嬉しいです。

  • 契約
  • named let
  • 動的束縛

ここでいう契約とは、関数の入出力の性質を関数定義の際に宣言しておくことで、関数を呼び出しを実行する際に入出力の値を検証し、違反していた場合にエラーを投げる言語機能のことです。余談ですが、契約と型は対応する概念です。契約の検証は実行時に行いますが、型検査はコンパイル時に行います。静的に型をつける言語では関数定義の際にその型を宣言することで、関数呼び出しのあるコードの入出力の方を検証し、違反する呼び出しを特定します。 ContractのRacket Guideを貼っておきます。https://docs.racket-lang.org/guide/contracts.html

ここでは https://docs.racket-lang.org/reference/function-contracts.html の例をお借りして説明します。

> (define/contract (maybe-invert i b)
    (-> integer? boolean? integer?)
    (if b ( -i) i))
> (maybe-invert 1 #t)
-1
> (maybe-invert #f 1)
maybe-invert: contract violation
  expected: integer?
  given: #f
  in: the 1st argument of
      (-> integer? boolean? integer?)
  contract from: (function maybe-invert)
  blaming: top-level
   (assuming the contract is correct)
  at: eval:2:0

冒頭の define/contract で始まるS式では、関数定義をしつつ、その関数の契約を宣言します。二つの引数 i, b をとる関数 maybe-invert を定義していて、bがtruthyなら-iを返しbがfalthyならiを返す関数として実装しています。この関数の契約は(-> integer? boolean? integer?) と宣言されています。これはinteger?を満たす値とboolean?を満たす値を引数に取り、integer?を満たす値を返す関数である、と読みます。

(maybe-invert 1 #t) という関数呼び出し式では第一引数に 1, 第二引数に #t を渡しているので入力に関する契約を満たしていて、返り値は-1になるので出力に関する契約も満たしています。そのためエラーが出ずに何事もなく計算結果の -1が表示されます。

一方で、 (maybe-invert #f 1) という関数呼び出しでは引数の順序を間違えて渡しているようです。第一引数にfalseを表す #f を渡しています。#finteger? を満たさない(そういうふうに integer?が定義されている)のでRacket処理系は契約に違反している旨をエラーとして表示しています。

こういうのが契約です。ちゃんと確認していませんが、PHPで型注釈を書いた際にも実行時の検査が行われるそうなのでPHPは契約を言語機能としてサポートしているといえそうです。他にはD言語も契約をサポートします。

入力値のバリデーションはコードを書くときに当たり前に行う作業ですが、それをシステマチックに行うためのフレームワークを言語が提供してくれるのは魅力的だと思っています。また、契約は型システムや漸進的型つけとも密接に関わりがある楽しい概念です。

次はnamed-letです。これについても公式ドキュメントを貼っておきます。https://docs.racket-lang.org/reference/let.html#%28form._%28%28lib._racket%2Fprivate%2Fletstx-scheme..rkt%29._let%29%29

RacketやSchemeなどのLisp系言語で勉強しているとwhileやforは習いませんが、再帰を習います。例えば整数のリストを受け取って、その和を返す関数sumを定義するにはこんな感じで書きます。

> (define (sum lst)
          (if (nil? lst)
              0
              (+ (car lst) (sum (cdr lst)))))
> (sum '(1 2 3 4 5 6 7 8 9 10))
55

nil?はリストが空を判定し、carはリストの先頭要素をとる関数で、cdrはリストの先頭を除いた部分をとる関数です。先頭の値とそれ以降の和をたすことで、リスト全体の和を得ています。

このように再帰があればループはかけるのですが、関数を定義してその直後にその関数を呼び出す、そしてその後その関数は使わない、みたいなケースがしばしばあります。そういうときに役立つのがnamed-letです。以下のように再帰関数の定義と関数呼び出しを同時に行うことができます。

> (let sum ((lst '(1 2 3 4 5 6 7 8 9 10))
       (if (nil? lst)
           0
           (+ (car lst) (sum (cdr lst))))))
55

再帰関数を使ってループを書くことでループの中での再代入を避けることができます。そのためループ不変条件を把握するのが楽になります。そしてnamed-letを使うことで、不要な関数定義を省くことができます。スコープにある変数は少ないほど嬉しいです。ここだけで使う再帰関数なんだということが一目でわかります。あと書いてみるとわかるのですが、named-letを書くととても気分がいいです。

最後は動的束縛です。emacs lispの変数束縛は動的だということで有名ですし、最近だとReactのContextが動的束縛っぽいかなと思います。やはり公式ドキュメントのリンクを貼っておきます。https://docs.racket-lang.org/guide/parameterize.html

誤解を恐れながらいうと、環境変数みたいなやつをコードの中で設定できる機能です。公式ドキュメントの例をそのまま貼り付けます。

> (parameterize ([error-print-width 5])
    (car (expt 10 1024)))
car: contract violation
  expected: pair?
  given: 10...
> (parameterize ([error-print-width 10])
    (car (expt 10 1024)))
car: contract violation
  expected: pair?
  given: 1000000...

REPLで2回プログラムを実行しています。(expt x y)xy 乗です。それのcarを取ろうとしています(carはリストの先頭要素を返す関数でした)が、(expt x y)の計算結果は数値であってリストでないのでcarの契約に違反します。そのためエラーメッセージが表示されています。今回フォーカスしたいのは動的束縛です。parameterizeerror-print-widthの値をそれぞれの実行で 510に指定しています。その結果表示されるエラーメッセージの幅が5になったり10になったりしています(10...1000000...)。おそらくエラーメッセージを表示する関数の中で error-print-widthが参照されているのでしょう。

このように関数を呼び出すタイミング側でその振る舞いを変えられるのが動的束縛の旨みです。関数の引数で渡す必要がないので、動的変数を参照する関数と設定する関数の間の関数たちが余分な引数を取らなくても良いわけです。

グローバル変数を使ってもこのような引数を介さない設定はできますが、動的束縛を使う方が衛生的です。例えば一つのプログラムの中でグローバル変数の値を変えたい場合、グローバル変数の値を書き換えることになります。これは悪名高い可変なグローバル変数を使うことを意味します。可変なグローバル変数は無関係に見えるプログラムの実行順序がクリティカルにプログラムの振る舞いを左右するのでよくないです。

一方で動的束縛では関数呼び出しごとに値を設定するため実行順序のことは気にしなくて良いです。値を設定した呼び出しの範囲下ではそれが反映されるし、その範囲外ではその設定は無効化されます。このように衛生的に、かつ疲れない形で広い範囲で参照する値を設定できることがグローバル変数と比較した際のメリットだと思います。

動的束縛される変数を参照する場合、呼び出し側の不慮の事故によって変数の定義もれでプログラムが正常に動作しないかもしれません。環境変数を設定し忘れるとアプリケーションが動かないのと同じです。こういう事故は実行する前、例えばコンパイルしたり型検査のタイミングで気がつけると嬉しいですよね。

Racketでこの課題を解決できるかは知らないのですが、エフェクトハンドラとエフェクトシステムを使えば型検査の中で解決できます! その説明をしたい気持ちが溢れているのですが、そろそろ日を跨ぎそうなので興味がある方は僕に声をかけてくれると嬉しいです。

プログラミング言語の言語機能、いいですよね。言語設計者が思う表現のベストプラクティスが詰まっていて触れるたびに嬉しくなります。

明日のアドベントカレンダーは冷静沈着なTepiさんがTextのJetpack Composeで画像表示した話を書く予定とのことです。 Jetpack Composeは名前しか聞いたことがないので、新しい概念を見られそうで楽しみです!

それでは良いクリスマスを!

2023-10-28

ジャンププラスの表示がどなっているか気になって開発者ツールでみたらなんかすごかった。アプリケーションのストレージにページをバラバラにしてパッチワークみたいにしたデータがあって、恐らくそれをいい感じに繋ぎ合わせて表示する感じでcanvas要素を使っているみたい。スクレイピングとかで漫画を引っこ抜かれないための仕組みなんだろうし、パッとわかるものではないだろうけど気になる。はてなのGigaViewrが使われているとのこと。ヒントとか開発話みたいなものが聞けないかなと思ってポッドキャストを聞いている。

2023-10-27

https とかで通信をするときはローカルにインストールされているCA証明書を参照する。 ubuntuの場合 /etc/ssl/certs/ca-certificates.crt がそれで、適当に名前を変えるとhttpsでcurlできなくなる。

🐧 curl https://pepabo.com -vvv
*   Trying 13.225.183.31:443...
* Connected to pepabo.com (13.225.183.31) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* error setting certificate file: /etc/ssl/certs/ca-certificates.crt
* Closing connection 0
curl: (77) error setting certificate file: /etc/ssl/certs/ca-certificates.crt

2023-09振り返り

今月はインカレの運営準備と仕事を頑張った。またカレーとボルダリングを始めた。

インカレでは人に仕事を回す仕事でしっかりキャパオーバーした経験をできた。おかげで価値観を少し変えることができて、瞬殺できるタスクはすぐに瞬殺するのが楽だと思えるようになったし、瞬殺できない仕事もなんとか小さくしようともがけるようになった。この辺りは仕事でも意識しているところで、二つのサンプルがうまいこと僕の中で補い合って学びを得る機械になってくれた形だと思う。どちらもちゃんとした人たちが一緒に仕事をしてくれてフィードバックもくれる環境だから実現しているのだと思う。感謝だし、この環境に身をおけた運と自分を褒めてあげたい。

カレーもオリエンテーリングと職場の人たちが相補的に絡み合って好きになり始めた。ボルダリングもそうか。今の僕は職場とオリエン界隈の二つのコミュニティに支えられて生活を営んでいるみたいだ。充実している。いいこと。

ここまではいわゆるソフトスキルみたいな仕事をする上での能力とか趣味の話だったけど、純粋な技術の話でいうとWebアプリケーション開発のレイヤが低めのところが(どのくらいかわからないけど)強くなった。Rackみたいな構成とかDNSの話(間接参照は偉大だ)、アーキテクチャの成長のさせ方とかを新たに腹落ちさせた。

10月は趣味を深めつつ、ソフトスキルっぽい面ではきっちり仕上げるところを重視する。ガッとやる。技術はネットワークとかを深められると嬉しい。趣味は今のを深めていく。ボルダリングは週2回いけるといいなと思う。

GASでスライド生成

Google Slidesで作成したテンプレートファイルに文字列を置換する形でスライドを生成するスクリプトをChat GPTに書いてもらったのでメモ。

+--------------------------------+
|                                |
|  {{氏名}}                      |
|                                |
|                                |
|     {{住所}}                   |
+--------------------------------+

みたいなスライドを作って、

氏名,住所
佐藤太郎,日本のどこか
田中一郎,東京のどこか

みたいなスプレッドシートでスクリプトを実行すると以下のようなスライドをテンプレのあるスライドに追記する。

+--------------------------------+
|                                |
|  佐藤太郎                      |
|                                |
|                                |
|     日本のどこか               |
+--------------------------------+

+--------------------------------+
|                                |
|  田中一郎                      |
|                                |
|                                |
|     東京のどこか               |
+--------------------------------+

スクリプトは以下。

function createSlidesFromSheet() {
  var slideId = 'ここにスライドのIDを入れる'
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  var data = sheet.getDataRange().getValues();
  
  var slide = SlidesApp.openById(slideId);
  var templateSlide = slide.getSlides()[0]; // テンプレートとして使う最初のスライドを取得
  
  var headers = data[0]; // ヘッダー行を取得

  // データ行をループ
  for (var i = 1; i < data.length; i++) {
    var row = data[i];
    var newSlide = slide.appendSlide(templateSlide); // テンプレートスライドをコピー
    
    var shapes = newSlide.getShapes();
    for (var j = 0; j < shapes.length; j++) {
      var shape = shapes[j];
      var text = shape.getText().asString();
      
      // スプレッドシートのヘッダーを参照して、対応するデータでプレースホルダーを置き換える
      for (var k = 0; k < headers.length; k++) {
        var placeholder = '{{' + headers[k] + '}}';
        text = text.replace(placeholder, row[k]);
      }
      
      shape.getText().setText(text);
    }
  }
}

2023-09-09

tomlもyamlも難しいので、みんなjsonを書けばいいのにと思う。 Goとか静的型付け言語とかが流行って、読みやすさの意義が重要視されている時代なはずなのに、設定ファイルは書きやすさとか、見栄えの良さが重視されていそうなフォーマットが人気なのが解せない。 jsonがナウいんじゃないかと思うし、好みとしてもjsonがいいなと思う。

jsonは誤解することが一番少なそうだし、みんな何が嫌なんだろう。編集がちょっとたいへんとか? そんなに設定ファイルを編集している時点で大変なことになっているのだから、それは妥当な大変さ何じゃないかと思うのだけど。

契約と型検査

プログラミングの型検査・契約・テストについて書きます。お酒を飲みながら書きました。注意は払ったつもりですが、変なところがあるかもしれません。

  • 契約とは何か
    • 例を含める
  • どんな嬉しさがあるか
  • 型検査との兼ね合い

契約 (contract) とはプラグラムの関数の入出力に関する規約のことです。例えば整数の割り算をする div 関数は、二つの整数を受け取って商を返す関数だとしましょう。このとき、入力の二つの値は

  1. どちらも整数
  2. 二つ目の整数は0ではない

ことが求められます。また、整数の割り算を行なっているので、div(a,b) の値は例えば

  1. a の絶対値以下であること

が求められます(もっと細かい要求をしても良いでしょうが)。箇条書きで示したような div 関数について求められる性質のことを契約とよぶことが多いです。

ここまでで例を示しました。例のことは忘れて一旦抽象的な定義を試みましょう。契約は関数を呼び出す側と呼ばれる定義の間の約束事です。関数を正しく呼べば正しい結果を返すことを規定します。どんな呼び出しが正しくて、正しい呼び出しをされたと仮定とした上でどんな結果が正しいかを規定するのが契約です。

先ほどの例を考えると、 div 関数の第二引数に0を渡すのは呼び出し側の契約違反で、div(168,4) の結果が42にならないのは関数定義の契約違反です。契約は、関数呼び出しにおける呼び出し側と定義側との間の規約を表明し、検査します。

多くの言語でコードを書く際は、関数の冒頭で引数のチェックをして、呼び出し側の責任を追及するスタイルでコードを書くことになりがちです。言語によっては組み込みの機能でいい感じに契約を書いて、実行時に検査できます。 D言語とかRacketがいい例です。

例えばRacketだと、モジュールでエクスポートする関数を表明する箇所で、関数の契約を書きます。完全に雰囲気ですが、おそらくこんな感じで書くんじゃないかったかと思います。正確にはRacket GuideかRacket Referenceをご覧ください。

#lang racket

(export
  (div (-> (and int (lambda (x) (< 0 x) )) int  int)))

...

(define (div a b)
  (/ a b))

契約を書いておくと何が嬉しいでしょうか。問題の切り分けが楽になることが嬉しいですね。Blame shiftingができます。みなさん人生で一回は Segmentation fault とか Null pointer exception とか car: cannot apply for nil みたなエラーに遭遇したことがあるかと思います。これらのエラーは契約をちゃんと関数に書いていないから起きるはずで、まともなライブラリを使っていればあまりみないはずです。これらのエラーが出たときには割と深く絶望して、どの関数が悪さをしているか探す旅が始まります。

もし真面目に契約を書いていれば関数が許さないnilを受け取った時点で呼び出し側に責任があることを即座にエラーを起こしたり、変な結果を返しそうになったら返す直前に同様にエラーを起こします。

これが契約の嬉しさです。反対に、関数を動かして契約違反で怒られない限り、その契約を実装が遵守していることが保証されます。これもまた契約の嬉しさの一つです。契約はプロダクション環境で生きているドキュメントとして機能します。

テストとの違いは、契約は実行時に検査をすることが大きいでしょう。テストはテストを走らせるときにしか検証を行いませんが、契約は実行時にも検査を行います。プロダクション環境で関数を呼び出すときにも契約は検査されるのです。

契約はいいものであることがわかったと思います。関数定義の性質を保証して、何かあればすぐに検出し、何もなければ、すべてのそれまでの実行で違反したケースがないことを保証します。

この観点で見たときに、型検査はテストよりも契約に近しい存在だと言えるでしょう。型検査は、コンパイルのたびに型制約が満たされていることを検証します。健全な(まともな)型システムでは、型検査をパスしたプログラムは実行時に型エラーを起こさないことが保証されます (この保証がある型システムのことを健全だというので順番は逆ですが)。型検査では、関数の入力として渡される値が、関数が期待する条件を満たすことを検査しますし、関数が返す値が宣言にあっていることも検査します。契約と対比させると、契約は実行時に検査を行う一方で、型検査はコンパイル時に行うという感じです。

逆にいうと、契約は型検査で静的にやろうとする検査を実行時まで遅延したものです。実行時には任意の計算をできるので表現力は契約の方が高いです。例えば引数の値が10と20の間である、みたいなことを検査するのは型検査では大変ですが、契約ならちょちょいのちょいです。

じゃあ契約だけでいいじゃないか、型検査なんてやめてしまえ!という話になるかというと、そういうわけにもいきません。型検査には契約にないよさがあります。

型検査の良さは静的に検証が済むことにあります。つまり、以下の二つを型検査は満たします

  1. 全ての実行について、検証結果が成立する
  2. プログラムを実行しないでも検証を行える

まず一つ目について。契約やテストでは、実行した場合については動作を検証してくれますが、それとは違う値については特に保証をしてくれません。だから境界テストみたいな、人間が上手に動かす技法が使われるのでしょう。型検査では、ある程度検証内容を抽象的にしたり保守的にすることで、全ての実行に対する性質を保証します。 intが返る関数は決してfloatを返さないことを型検査では保証できます。

抽象化や保守的な検査を行う二つ目の恩恵として、実行しないでも検証できることが挙げられます。本番環境で動かさないとわからなかったり、テスト環境をせっせと用意する必要は、型検査では生じません。

型検査や契約の有用性を主張してきましたが、それでもテストは必要です。実際に色々な具体的なケースで動かすことでわかることは多いでしょう。型検査は動かさないでもわかることを検証して、契約は動かしたらわかることを検証します。テストはプログラムを動かしてみて検証します。型や契約だけではプログラムを動かすことはありません。そこが大きな違いなはずです。

それでも全ての異常の検知をテストでまかなう必要もないはずです。契約や型検査は保証をする仕組みとして優秀です。基本的な保証は契約や型検査で済ませて、本当に際どいところをテストでカバーする、みたいな役割分担をすると幸せになれるんじゃないでしょうか。

dotfilesのセットアップスクリプトを書いた

dotfilesをGitHubで管理しているのだが、今までは設定ファイルの管理だけで、インストールは手動で行なっていた。設定ファイルを使いまわせるだけでだいぶ便利なんだけど、コンテナ環境の中で作業したくなると、手動インストールに耐えられなくなる。そこで重い腰を上げてセットアップを自動化した。

git のインストールと以下の実行でok。 nvimとかfishとかcargoとかが入る。

git clone https://github.com/naoyafurudono/dotfiles.git
bash dotfiles/setup.sh

test

arm環境かつubuntuだけでしか動かしていないが、dockerコンテナでセットアップスクリプトを動かして、正常に終了するかを確認するテストを書いた。

この手のセットアップスクリプトのテストって、世の中ではどのようにテストしているんだろう。以下の難しさがテストを大変にしている気がする。

  • 環境依存な部分をうまく吸収する必要があること
  • インストール失敗の判定が難しいこと

2023-07-29

ARM64でいろんなサイズの整数の足し算をする方法を整理する。サイズごとに演算が用意されているわけではなく、レジスタの指定方法(x,wの使い分け)や、レジスタのコンテンツをいい感じにすることで、 byte, half, word, double wordの足し算を実現する(と思ってよさそう)。

  • レジスタどうしの足し算では、destinationとその他の二つのsourceレジスタのサイズが同じじゃないといけない。結果のサイズがdouble wordならXnを使えばいいし、word以下ならWnを使う。
  • レジスタのコンテンツをいい感じにする方法は二つありそう
    • 適当なマスクとandを取る
    • 専用の命令を使う。二種類観測した。
      • Armv8-A Instruction Set Architectureが優しい。7.4節Extension and saturationに説明がある
      • 一つ目は拡張命令を使う方法。符号{あり,なし}と拡張前のサイズについて、それぞれ定義された拡張命令を使うと、destinationレジスタにsourceレジスタを拡張したビット列がセットされる。
      • 二つ目は足し算で追加の引数として拡張方法を指定する方法。拡張命令と似たような感じのenumが定義されていて、それをadd命令のsourceレジスタの後にしていすると第二sourceレジスタを指定した方法で拡張した上で足し算する。

朝はお茶漬けを久しぶりに食べた。朝にお米を食べようと思うと、やっぱりお茶漬けがいい。子供の頃に抱いた感覚が長持ちしている。一時期パンに手を出したこともあったけど、なかなか難しい。食パンは口の中の水分を持っていくので惣菜とかが一緒にないとちょっと難しい。小倉トーストとかは好きなので、全くダメというわけではないと思うのだけど。

2023-07-22

コンパイラの実装で、次はポインタ算術をやる。演算子がオーバーローディングされているので型検査が必要。今は環境とかのデータ構造を設計する気力がなくて、パソコンを前にふにゃっとしている。お昼ご飯食べたら考える。

興味本位でコンパイルしたプログラムの実行速度を測ってみた。時間計算量が試数時間なfibの実装(あのみんな大好きなナイーブなやつ)で、 fib(40)を計算してみた。自作コンパイラだとuser時間が1.3secくらいなのに対して、 gccのO3でuser時間が0.3secくらいだった。 O3を指定しないと(多分O2)0.5secくらい。 Inliningとかは効かないだろうし、ループ周りでの最適化もできないはず。スタックマシンではなくレジスタマシンとして動かすことで(無駄なpush/popを排除することで) O2くらいの速さになってる感じかな。 O3では何が早くなってるんだろう。気が向いたら調べるか、実装するかしたい。

お昼を食べて買い物に行ったあと、型検査器を実装した。がんばった。 strcmpがboolを返すと思い込むやつをやって時間を溶かした。ランニングをしてオーバーロードに取り組む。今はintもポインタも64bitにしているけど、abiをみたらintは32bitっぽいので合わせる。オーバーロードは楽しそうだけどintを32bitにするのが面倒。 ASTに型を行き渡らせたので難しくはないはず。

買い物ではズボンを買いに行った。涼しい長ズボンが欲しかったので。初めて買う感じのやつを変えて満足している。サンダルも欲しかったけど、僕の期待が矛盾していることに気がついたので断念。来年は期待を変えられることを願って今年はスニーカを履き続ける。

Cとかその辺の関数型言語を使っていると欲しくなる言語機能第1位は動的束縛だという説を提唱したい。代数的エフェクトほどのことを普段使いに人類は求めているとは僕には思えなくて (普段使いと言ったのは、凝った仕組みを作る場合はのぞきたい気持ちの表れ)動的束縛が欲しいんじゃないだろうか。オブジェクト指向でインスタンス変数を使うのもこの用途が多いんじゃないかと勝手に思っているんだけどどうだろう。

とにかく、動的束縛が欲しい。動的束縛はいいぞの会を結成するまである。

Uを叩くとメチって音がする。髪の毛でも入り込んでしまったかもしれない。割とストレス。

2023-07-21

https://github.com/naoyafurudono/comp

早いもので作り始めてから2週間たったみたい。もっとサクサク進むと思っていたのだけど、しばらく楽しめそう。

自作CコンパイラでCのプログラムの一部をコンパイルできるようになった。これまでは変数宣言をできなかったので型宣言のあるmain関数を読めなかったが、 intだけ許容するようになったので、初めてCのプログラムをコンパイラが受け付けたことになる。

こんなコードをコンパイルすると、

int main()
{ 
  int a; int b; int i;
  a=0; b=1; i=0;
  while(i < 10)
  { 
    int t; 
    t=a+b; a=b; b=t; 
    i=i+1; 
  }
  return a;
}

こんなアセンブリを吐く。 SSAみたいな中間表現を挟んでレジスタ割り付けを真面目にやるのはいつがいいんだろうか。

.globl _main
.text
.balign 4
_main:
    stp x29, x30, [SP, #-16]!
    mov x29, SP
    sub SP, SP, #32
    mov x0, #-16
    str x0, [SP, #-16]!
    mov x0, #0
    str x0, [SP, #-16]!
    ldr x0, [SP], #16
    ldr x1, [SP], #16
    str x0, [x29, x1]
    str x0, [SP, #-16]!
    mov x0, #-24
    str x0, [SP, #-16]!
    mov x0, #1
    str x0, [SP, #-16]!
    ldr x0, [SP], #16
    ldr x1, [SP], #16
    str x0, [x29, x1]
    str x0, [SP, #-16]!
    mov x0, #-32
    str x0, [SP, #-16]!
    mov x0, #0
    str x0, [SP, #-16]!
    ldr x0, [SP], #16
    ldr x1, [SP], #16
    str x0, [x29, x1]
    str x0, [SP, #-16]!
.L0:
    mov x1, -32
    ldr x0, [x29, x1]
    str x0, [SP, #-16]!
    mov x0, #10
    str x0, [SP, #-16]!
    ldr x1, [SP], #16
    ldr x0, [SP], #16
    cmp x0, x1
    cset x0, lt
    str x0, [SP, #-16]!
    ldr x0, [SP], #16
    cbz x0, .L1
    mov x0, #-40
    str x0, [SP, #-16]!
    mov x1, -16
    ldr x0, [x29, x1]
    str x0, [SP, #-16]!
    mov x1, -24
    ldr x0, [x29, x1]
    str x0, [SP, #-16]!
    ldr x1, [SP], #16
    ldr x0, [SP], #16
    add x0, x0, x1
    str x0, [SP, #-16]!
    ldr x0, [SP], #16
    ldr x1, [SP], #16
    str x0, [x29, x1]
    str x0, [SP, #-16]!
    mov x0, #-16
    str x0, [SP, #-16]!
    mov x1, -24
    ldr x0, [x29, x1]
    str x0, [SP, #-16]!
    ldr x0, [SP], #16
    ldr x1, [SP], #16
    str x0, [x29, x1]
    str x0, [SP, #-16]!
    mov x0, #-24
    str x0, [SP, #-16]!
    mov x1, -40
    ldr x0, [x29, x1]
    str x0, [SP, #-16]!
    ldr x0, [SP], #16
    ldr x1, [SP], #16
    str x0, [x29, x1]
    str x0, [SP, #-16]!
    mov x0, #-32
    str x0, [SP, #-16]!
    mov x1, -32
    ldr x0, [x29, x1]
    str x0, [SP, #-16]!
    mov x0, #1
    str x0, [SP, #-16]!
    ldr x1, [SP], #16
    ldr x0, [SP], #16
    add x0, x0, x1
    str x0, [SP, #-16]!
    ldr x0, [SP], #16
    ldr x1, [SP], #16
    str x0, [x29, x1]
    str x0, [SP, #-16]!
    b .L0
.L1:
    mov x1, -16
    ldr x0, [x29, x1]
    str x0, [SP, #-16]!
    ldr x0, [SP], #16
    mov SP, x29
    ldp x29, x30, [SP], #16
    ret
    mov SP, x29
    ldp x29, x30, [SP], #16
    ret

2023-07-19

実装しているCコンパイラ で関数定義をできるようになった🎉。今までも既存のコンパイラで作ったオブジェクトファイルとリンクすれば呼び出すことはできていたが、自分で定義した関数がある程度ちゃんと呼べるようになった。

まだ型がlongしかないので道は長い。

https://github.com/atweiden/fzf-extras ここが良さげ。

2023-07-17

Cコンパイラをarm macで実装しているのだけど、a=b=0; return b;"みたいなコードがセグメント違反で落ちる。 lldbで調べてみると、main関数は正しい値をw0にセットしてretできていそうなんだけどその後の動的リンクされたコードでエラーが発生しているみたい。設定する値を変えるとエラーにならずに実行が終了する。謎である。

ABIを守っていないのが悪さをしているかも。

土曜日はパソコンしてた。お昼頃に連絡が来ていたことに気がついて、翌日の約束をした。

勢いで日曜日は丹沢に行くことになって、卒業ぶりに会った友達と2時間くらいの登山をしてきた。コースタイムが4時間だったのでいいペースだった。最近登山をしておらずペースタイム通りの時間でいく想定をしてしまっており、思ったよりもあっさりと終わってしまった。友達の靴が下山した後の道で壊れてしまったのでサンダルを貸して歩くことに。風呂に入ってからはま寿司で3時間くらいお茶を飲んで帰宅した。

日曜日は海の日なので海を目指して部活の後輩と走った。天王洲アイルが海の近くだったのでそこまで行ったのだが、思ったよりも海っぽくなかったのが残念。久しぶりに走ったので走り始めがだいぶ辛かった。

久しぶりの人と会うと時間の流れを感じる。

arm64のメモ

Cコンパイラを実装するためにarm64のことを調べている。この記事はそのメモ。

よくあるソフトウェアのライブラリについているドキュメントとは毛色が違う。

Arm macを使っている人の話です。

  • 既存コンパイラがはくアセンブリを覗く方法
    • アセンブリをはく: gcc -O0 -S
    • ディスアセンブル: otool -vVt
  • デバッガで調査する

アセンブリ命令って割と体系的になってなくてやばいイメージがあったけど、 arm64は秩序がある程度あるように感じている。まだ深淵を覗けていないだけかもしれないが。汎用レジスタの名前が簡単なことが大きいかもしれない。

2023-07-08

椅子に座るほどの元気がなくてもパソコン座れるので楽しい。気軽にコーディングできる。

cコンパイラ実装するやつは、今からローカル変数の定義に入るところ。ここまでで、四則演算と比較をコンパイルできるようになった。 armでやっているのだが今のところそんなにx86と違わない。 push/popでスタックポインタがarmだと16byte境界にしか設定できないとか、割り算がシンプルだとかくらいの違いしか観測していない。

そういえばenumは値だった。変数名みたいにコンパイラがよしなにするやつというイメージが強くて戸惑ってしまった。今回初めてcファイルを頭を使って分割した。定義と宣言の違いを認識して活用する日強がある。

パーサ前はサクッとできて、コード生成も一応形にはしたのだが、生成したアセンブリをアセンブルして実行するとセグフォで落ちる。 x29にSPをmovするだめみたい。x29以外にしたらどうだろうと思い、x9を使ってみたらセグフォのタイミングが変わった。ちゃんと追ってないので明日起きたら追ってみよう。

とりあえず、armのマニュアルを落とした。めちゃ長いけど良さげ。ソフトウェアと違って、ライブラリが無い分こういうふうに一つのでかいドキュメントが提供されるのはなんかそうなんだろうな、という感じがする。Web版よりも検索しやすいのはいいこと。今日はこれくらいにしよう。

ちなみに今はラフロイグ10年を飲んでいる。いいでしょ。

動画を見たり手を動かすことが増えた。あとは仕事でドキュメントを読む時間が増えて、活字が安定供給されているのが影響しているかも。

とはいえ仕事で読むドキュメントは、読み物としてはそんなに面白く無いことが多い。その分手を動かしてものを作るのが楽しいからいいのだけど、小説を読む楽しみとか、心に響く論文を読む感覚が足りない。小説とか論文とかに飽きてきたのはあるのかもしれない。残念ながら深掘りできる人間ではなかったのか。広く学ぶ好奇心とかがあるわけでもないのだけど。

話題になっていたのは当時から認識していたが、ずっとみていなかったユーフォを見た。特に第一シーズンが好きで、上手くなりたい、のあたりが響いている。僕がある程度まじめに考えて方向性を決めるときには、上手くなりたいが奥底に合ったなと再認識させられた。

頑張っているときはわからないけど、少し環境が変わると上手くなっていたことがわかる。加えて、そのときになって周りから、それが上手い人というラベルを貼られることに気がつくし、振り返ると上手くなったなと気がつく。上手くなりたいと憧れた、あの頃の自分からすると今の自分は割といい線行ってるんじゃないかと思う側面がある。全部が全部上手くなってるわけじゃないけど。

この頃は、上手くなった先に何があるのかということをちらほら考える。幸せがあると思っていたのだが、実際のところはどうなんだろうか。今は体力と好奇心と自尊心がボトルネックになって、ボトルネックになってることがそれらを削っていくんじゃないかと思う。また、自分が上手くなるのにかかる時間が実感を通して認識できたことで、自分の限界を認識するようになるのだろう(僕はまだ認識してない。アカデミアのスターにも総理大臣にも、経済界のドンにも縁があればなれると思ってる。巡り合わせに依ると思ってる時点で擦れているのかもしれないけど)。

僕はアニメを見て、アニメの感想ではなく内省をアウトプットしたくなってしまうので自分から進んでアニメの話をしたくならない。聞かせるのは悪いなという気持ちとなんか恥ずかしい気持ちがある。日付が変わってしまいそうなのでこのくらいで。ところでフロムザバレルは甘い。干し芋っぽい風味がする。

明日はランニングしよう。午前中に走ってみる。

2023-07-02

末尾位置での関数呼び出しは、継続渡し形式ではcaleeに渡す継続をcallerが大きくしないで、 callerのcallerから渡されたものをそのまま渡す点で特徴的だ。レジスタマシンではcallerのスタック領域をcalleeが上書きして使うことに対応するのだろう。こういうcall命令はどのくらい実用されているのだろうか。

https://www.sigbus.info/compilerbook これを読んでいる。

高校の化学の授業でナトリウムをどうやって保存するべきかと先生が問いを投げて、追い討ちをかけるようにそのまま先生が水筒ですか、と言ったのをたまに思い出す。確か灯油とかに放り込んでおくのが定石だった気がする。

今日は水筒を買った。会社で水をもっと飲みやすくなる。水筒にはお茶もコーヒーも入れないつもり。レジで「ご自宅用ですか?」と聞かれたのが印象的。プレゼントか否かを暗に問われたのだと思ってそうですと言ったのだが正しく意図を汲み取れていたのだろうか。家で水筒使うことなかなかなさそうなので明には嘘をついてしまった。

意図が自明ではない発言にはその背景を添えて欲しいし、自分も添えるように気をつけよう。

報酬主義を超えてを読み始めた。思ったよりも面白い。ある程度、当たり前の主張をさも常識を疑って得られた結論みたいな書き方をするけど、それでも僕の立脚する価値観を疑うようなことも書いてありそうなので期待している。

2023-07-01

envsubstを使うと標準入力の環境変数の参照っぽいところに環境変数の値を埋め込んだテキストを吐ける。 readmeにコマンドのヘルプメッセージを載せたいときに便利。例: https://github.com/naoyafurudono/tools/tree/main/cl

gitで管理しているファイルのパスが得られる。 git grepみたいに管理対象だけに興味があるときに便利そう。 fzfとの相性が良さげ。

2023-06-30

すでにgit管理されているファイルをgitignoreでマッチするようにしても、管理から外れない。外すためには以下のようにすると良い。

git rm -r --cached .
git add .
git commit -m 'update with .gitignore'

2023-06-29

crate.ioに初めてコードを登録した。cargo install で入るから便利。ちょっとしたCLIツール書く体験がなかなか良い。 copilotが面倒なことやってくれる。

crates.io

Railsとか書いてると深いところに元からあるファイルをコピーしたいことがちょいちょいある。そのときに長いパスを2回打ちたくないので作った。

2023-06-24

macのタイムゾーンの設定が狂っていて、今日の投稿がこれと前回の二つに分かれてしまった。

今日はmacの設定と、以降に伴って壊れたちょっとしたCLIツールの修正した。昔Rustで雑に書いたもので、エラー処理が辛そうだったので少し勉強して改善した。 anyhowを使ってみたい気持ちが湧いたけど、まずは基本的なところからということでトレイトオブジェクトを使うことにした。type Result<T> = std::Result<T, dyn Error>みたいな感じにすることで、いろんなエラーを孕んだresultに?を使うことができて幸せ。

一箇所ムムッとなったのが、クロージャの中で?を使う箇所。dyn Errorの気持ちで書いていたら、推論器はもっと具体的な型を推論していたみたいで、二つ目のエラー型の式に対して?したら型エラーになってしまった。こういうときはアノテーションをつければ良い。 Rustの型推論の割り切り方に慣れていない。

2023-06-23

macを買って環境構築をしているのでメモしていく。

  • capslock -> eisu toggle
    • システム環境設定 > キーボード > テキスト入力 > 入力ソース > Caps LockキーでABC入力モードに切り替える
    • 最近のmacは組み込みの設定で変更できるようになった
  • 1Password
  • CLIツール: 以下の順で入れると良い。気が向いたらインストールスクリプトを書こう
    • brew
    • fish
    • nvim
    • .config
    • その他
  • gitでsshを使うための設定: この投稿のおまけを参考に
    • 鍵を生成
    • githubに公開鍵を登録
    • .ssh/configを書く

個人メモ:dotiflilesに書いてあるのでコピペは不要。

# for fish
function _ssh-keygen
  set name $argv[1]
  mkdir -p "$HOME/.ssh/$name"
  ssh-keygen -t ed25519 -f "$HOME/.ssh/$name/id_ed25519"
  chmod 600 "$HOME/.ssh/$name/id_ed25519"
end
#!/bin/bash

name=$1
mkdir -p "$HOME/.ssh/$name"
ssh-keygen -t ed25519 -f "$HOME/.ssh/$name/id_ed25519"
chmod 600 "$HOME/.ssh/$name/id_ed25519"

Host github.com
  User git
  IdentityFile /Users/naoyafurudono/.ssh/github/id_ed25519

Terraformでローカル環境の管理とかできないだろうか?

2023-06-10

共感できるところが多かったし、なるほどなと思うところもあった。なにより読んでて楽しかったので読んでよかった。

野崎まどの本を読むのは、野崎まど劇場を友達に借りたのが最初だった気がする。かれこれ10年くらい前の話かも知れない。

天気予報を確認したいのだけど、テレビを見たり朝スマホを確認する習慣がないし作る気もないので、なにか方法が必要。ちょうどラズパイが手に入ったので、毎朝どこかから引っ張ってきた天気予報を喋ってもらおうかと考えている。

読み上げは、voicevoxがいろいろいい感じなので試してみるつもり。ドキュメントがあまり充実してなさそうなのがたまにきず。やってみた系のブログ記事が有ったのでメモ https://ponkichi.blog/raspberry-voicevox/

天気予報はどこから取ってくるのがいいんだろう。とりあえず、傘が必要かどうかを知りたい。

CAMPHER-が主催した植山類さんの講演でyoutube配信があったので視聴した (youtube)。リンカの話はよく知らなかったので楽しかった。「『CSの大体の問題は間接参照で解決する』という言葉がある」という発言がなかなか面白かった。使いたくなるときに我慢して使わずに乗り越えられたら幸せになったりするだろうか。

2023-05-13

もともとbashで書いていたスクリプトを拡張したくなって、普段ならPythonで書くところを今日は新しいことをしてみようということでRustをつかってみることにした。

Rustはまったく初めてと言うわけではなくて、ちらちらドキュメントを読んだり、ノリで学校の課題をRustで書いてみたり(痛い目にあった)したことがある。所有権のあたりで苦労はあまりしなかった(というかPythonっぽくヒープを贅沢に使うコードを書いた)のだが、Result / Optionの変換で発狂しかけた(メソッドが定義されていることに思いをはせられずに自作してしまった)。

あまり苦労しなかったと言いつつ、所有権のあたりで困りはしている。参照を受け取るコンストラクタに、その場で作ったオブジェクトを渡したいときに、一旦変数に束縛しないと、渡すオブジェクトが即死してしまっていけない問題が面倒。名前をつけるまでもない、式が体をを表しているような値にまで名前をつかないといけないのはしんどい。コンストラクタが所有権を奪うようにすればよいのだろうか。いらないから参照にしたのだよな…。

今ではそこそこなれてきて、代数的データ型とまともな型検査器がついたPythonくらいの書き方はできるようになった。 Rustのライブラリがイケイケに作られていて(エラー処理周りは辛いと感じるけど)、プログラムを書いていて楽しい。すごく汚く書いてしまった。当面の目標はまともにかけるようになることかな?ちょっとしたCLIでは大差ないのかもしれないけど。

ブログのGitHubのプログラミング言語構成比がカオスなことになってきた。

ここ何日か胃腸炎でつらい。今日はだいぶ回復して、Rustを勉強できるくらいになったけど、まだつらみを感じる。とはいえ三食たべても大丈夫な体に戻りはしたので問題なし。何日か会社に行けなかったのがなかなか悲しい。

今日は以前もらい忘れた診断書を書いていただいた。前回の診療に対して診断書を出してもらうことはなんだか難しそうで、もう一度診察していただくことになってしまった。自分の中では治ったと思っていたけど、見ていただいたらまだ治っていないとのことだったし、今はそう感じる(流されやすい性格なのか?)ので、忠告していただけてよかったと思うことにしている。

病院の帰り道で食器のフリマ的なことを日本料理屋さんがやっていた。普段使っている食器を入れ替えるのかなにかで、安く売り出してくださっていたようだ。胃腸炎になるまえに、会社のランチで「おいしそうな自炊の写真はくろっぽい器が大切」とのアドバイスを頂いていたことを覚えていたので、これは運命だと思い食器を購入した。黒っぽい器はどんぶりしか出会えなかったけど、それがなかなかいい感じだし、白っぽい器もいい感じのにいくつか巡り会えたのでほくほくしている。

あとはおいしそうなご飯をつくればよい。

夕飯につかってみた。いい感じ。味噌汁のお椀(蓋付き)が小さいやつで、フリーズドライの味噌汁一杯分しか入らないやつ(褒めてる)。感動している。

(アマプラ)。グレンラガンの監督?が制作しているらしい。ノリが楽しい。

2023-04-16

午前中はChatGPTとかであそんでいた。ブログを書いていたおかげで、AIに食わせる自分の書いた文書に困らないのが嬉しい。

文書から自分の人柄を表現してもらって、それをもとにSNSで使えるアイコンを生成しようとしたのだけどうまくいかない。人柄の表現はそれっぽいのができるのだけど、表現が抽象的になってしまうので、そのままアイコン画像を生成につかうと小学生のときによんだ漫画にでてきそうな怪しくて怖い宗教っぽい画像がでてきてしまう。

インターネット上でのキャラクタに合わせたアイコンを提案してもらえたら楽しくないだろうか?

ちなみに生成できた画像はこんな感じ。改善のしようはいくらでもありそう。

DALL-Eで生成したもので、権利の問題はなさそう (2023年4月16日閲覧):

Starting today, users get full usage rights to commercialize the images they create with DALL·E, including the right to reprint, sell, and merchandise. This includes images they generated during the research preview.

本腰を入れて文書を読むときは印刷して読みたい。なので長いWebドキュメントを読むときは少し辛いし、論文を読むときは(本腰を入れて読むなら)印刷する。本はたいてい物理の書籍を買っていた。印刷するのが面倒だし、書き込みを残しやすいので。

ただ就職して出社するようになって、移動中に文書を読みたい機会が増えた。そんななかで本を持ち運ぶのは大変で、その日に読みうる範囲だけを持ち運びたくなった。そういう経緯でPDFみたいな印刷しやすい形式で購入できる場合は積極的に電子書籍を買うようになってきた。本のサイズが小さければこれまでと変わらず物理で買ってしまうのだが、面積が大きかったり鈍器の形をしているものは電子書籍を買う。

書き込みが残らなくなる問題は解決できていないが、書き込みたくなるほどまじめの読むことは少ないし、そこまで腰を吸えられるのは結局家の中のはずだ。今の生活ではそんなに困らないだろう。余談だが、EoPLを物理でもっていないのは数少ない書き込みが大切な機会を逸しているように思える。

SNSで情報発信するのがあまり得意ではない。もともと会話があまり得意ではなくて、インスタントに情報を発するのが苦手なのだと思う。なにか考えてから言葉にするのにある程度時間がかかるし、言葉にしてから吟味したくなってしまいもする。

お酒が入ると吟味したい気持ちがどこかにいってしまうからしゃべる量が増えるのだろう。

話を戻そう。SNSは思ったことをパッと書いてそのまま発信することを重視したようなUIをしているように思う。たぶんそういうところで苦手意識を持っている。とはいえ情報発信の場としてSNSがメジャーなのは間違いないだろうし、ここみたいなブログはほとんどの人にみられないだろう。ブログを更新したときにSNSにリンクを投稿すると良い気もするが、それはそれでSNSの苦手な部分がブログ投稿に侵食してきそうで嫌な感じもする。悩ましい。

2023-04-10

この頃「詳解システム・パフォーマンス」を読んでいる。チューニングをする機会はないのでツールの説明は読み飛ばしているが、それに入る前の基礎的な概念を勉強している。学部の授業でやった理論的な基礎から復習して、もう少し具体的な実装とか最近のアーキテクチャのこととかまで勉強できている気がする。

今からファイルシステムの章を読み始める。

2023-04-06

今まではレイテンシを減らすために改札をくぐった際の処理をローカルで行い、その後まったり同期する、みたいな方式をとっているという記事をみた記憶がある。

これからそのように凝ったことはしないで、改札機はクライアント、処理は中央のサーバで行うそうだ。時代が変わったのでそれでもいけるとか。なかなか驚きだ。どんなパフォーマンスチューニングやUXまわりの工夫がされているのだろうか。 Suicaの話は何回聞いてもロマンがあると感じる。運用開始が楽しみだ。

昔Fitbitを使っていたのだけど、日々の記録が安定していておもしろくなかったから一年ほどでやめてしまった。最近通知を腕で受け取りたいとか、ランニングのときにつける時計を変えたいみたいな欲求がでてきて探してみると、 Fitbitが良さげだったので再開することにした。Charge5を使っている。必要十分プラスアルファくらいの機能でいい感じ。ただSlack返信機能を誤作動させてしまったので対策したい。ぱっといじった感じでは無効化する設定を見つけられなかった。あとは腕時計をつけなれてなくてうっとうしく感じるのが欠点。

それ以外はばっちり。ランニングの記録もとれるし、通知もしっかり気がつける。ディスプレイの常時点灯を時間をしていして有効にできるので時計としても完璧につかえる。

2023-04-02

CPythonの開発者向けドキュメントがとてもよい。今はコンパイラの構成に関する部分を読んでいる。抽象構文木の扱いやメモリ管理の方針など、コンパイラ開発の方針を説明している。ドラゴンブックとかからは得られない、開発手法の知識を得られて幸せ。

好きな言語にドキュメントがたくさんあることは本当に嬉しい。

2023-03-31

東大の田浦先生が公開されている大規模ソフトウェアを探るがとてもよかった。 GDBを使って大きなソフトウェアの振る舞いを理解する方法を学べる。 2年ほど前に、こういう資料が喉から手が出るほどほしいと感じていた記憶がある。

今日はこのあと研究のことを少し考え、後輩とお酒を飲む。どちらも楽しみだ。

午前中には入社や書類を仕事をした。 Macのターミナルが重い。今まで使っていた研究室のでも重かったし、新しく使い始めたものでも重い。原因を調査中。アプリを起動するのも重いし、コマンド実行が終了してプロンプトが返るまでのレイテンシもある。

2023-03-19

最近AIが流行っていて、精度がすごいと話題で、生活が多かれ少なかれ変わっていくのだろうなという感じがする。ああいう機械学習ベースのAIは学習で直感を鍛えて、それに基づく出力をするものだと僕は解釈している。人間が頭を使うときには直感を論理とか議論で検証して、主張の穴を見つけて改善するサイクルを回すはずだ。今の機械学習っぽいAIは直感で得た主張を論理で解釈しないだろう(NECがそういうことを考えていそう。それができればAIはもっとつよくなるはずだし、そういうAIを見てみたい。

実現するためにはAIの入出力に使う「言語」に解釈を入れることと、それをもとにして論証を行うことが必要だろう。今のAIは抽象構文木を操作の対象にしていて、それを意味を解釈するようにしたらいいんじゃないかという想像だ。

今ある論理が世の中の事象を扱うのに十分かはわからないし、多分結構不足しているはずだ。機械学習AIのための論理体系の研究とかあったら楽しいだろう。NIIとかでやってないだろうか?

おととい届いたBig Peatがとてもおいしい。明日の研究室飲み会にもっていこう。ちょうど二年くらい前に部活の先輩に勧めていただいたのだが、懐具合の問題で飲めずにいた。少し無理してでも飲んでおいたほうが良かったかと思うくらいには好き。ストレートで飲んでもおいしいし、ハイボールにしてもおいしい。天才か? ぼくはスコッチウイスキー?が好きなのかもしれない。

学部2年のころ、確率論基礎の試験の前日にウイスキーを飲む会に誘っていただいたとき、ラフロイグ・ロアを味見させていただいて半分感動したのを覚えている。あのころは二十歳になりたてでお酒のおいしさは今よりも分かっていなかったのだが、それでもいいかんじなことは分かった。それからラフロイグ・ロアには縁がなかったのだが今気になっている。数量限定なようで、毎年11月ころに発売されそうな雰囲気がある。今もアマゾンで売ってるが、転売価格に見えるので時期がくるまで我慢しよう。

ちなみにBig Peatと一緒に勧めていただいたジョニーウォーカーのグリーンラベルはすでに飲んでいて、 やはり好きな感じだった。

使い勝手の良いデスクトップ向けウィンドウマネージャが欲しい

使い勝手のよいウィンドウマネージャがWindowsとかMacに搭載されることはないだろう。使うアプリケーションやブラウザ、入力デバイスやその使い方によって使い勝手が大きく変わるだろうから。 OSが決め打ちで提供できるものでは無いんじゃないかと思う。なので自分でまじめに使い勝手の良いものを考える必要がある。

先日オリエンテーリングのインカレの配信のお手伝いをしたのだが、そこで使っていたスイッチャがなかなかデスクトップでのウィンドウマネージャの機能として魅力的に感じた。

スイッチャにはいろんなウィンドウ(カメラからの入力や、図、合成用のテロップなど)が接続されていて、すべてが画面の下部に控えめに表示される。それらを見ながらスイッチャのオペレータが放送するためのウィンドウを選択、合成する。放送されるウィンドウは画面上部に大きく表示される。放送されるウィンドウの右に同じサイズでプレビューウィンドウも表示される。これらの違いは放送されるか否かだけで、合成のテストとかに使える。

これを実現するためには操作用のデバイス(このデバイスをスイッチャというようだ)と、大きなモニタが必要だ。

デスクトップのウィンドウマネージャでもスイッチャのように

  • すべてのウィンドウを表示しておいて
  • 本番+alphaくらいのウィンドウをメインエリアに表示する

と便利な気がする。単純にはいかないだろうけど、いい線行きそうだ。

懸念事項は以下の通り:

  • ウィンドウの個数が多くなりそう
  • ウィンドウサイズが固定ではない(アスペクト比)
  • ウィンドウの動的な生成とその配置方法が非自明

エフェクトハンドラの良さと実用性について

エフェクトハンドラで継続や代数的エフェクトを扱う必要性は一ミリもなくて、実用的にそれらが欲しくなることはないか、あるいは限られていてそこまで一般的な機能を提供する必要はないんじゃないかと感じている。

このあたりを議論するために

  1. エフェクトハンドラの嬉しさ
  2. 意味論の歴史的経緯
  3. 改善ポイント

を考える。

なお、この記事はとくに裏付けもなく書いている。気が向いたら裏付けをしようと思っているが、この記事の目的は僕の考えの整理であって、世に主張をしたいわけではない。

記事の内容は不正確なことを留意されたい。

エフェクトハンドラが実際的 (practical) なプログラミング言語でエンドユーザに使わせたくなるのは

  • エフェクトシステムと相性のよい意味論
  • 動的束縛

を提供したいからではないだろうか。エフェクトハンドラを言語に入れれば、それで表現できる操作は自動的にエフェクトシステムで追跡できるし、ハンドラを用いることでエフェクトをローカルに使えるのは特筆するべきだろう。汎用性とlocal reasoningのしやすさはエフェクトハンドラのもつ良い性質だと思う。

エフェクトハンドラで実現できる動的束縛はとても使い勝手が良い上に、エフェクトシステムで追跡することで使い勝手が上がりそうだ。動的束縛のためだけのエフェクトシステムではなく、もう少し凝ったことができるエフェクトシステムがつくとなお幸せだろうから、エフェクトハンドラみたいな抽象度の比較的高いフレームワークで実現するのは幸せなんじゃないかと感じる。

エフェクトハンドラと呼ばずに “algebraic effects” とか “algebraic effects and handlers” とか呼ぶ流派、時代がある。歴史的には

  • algebraic effects
  • algebraic effects and handlers
  • effect handlers

みたいな流れで登場したはずだ。最初はハンドラはなくて、モナドとかの話をするような人たちが副作用にモナドではない別の表現を与えようとしたんだったか。ここでいうモナドはモナド則とかを真面目に考えるような数学のモナド。代数的エフェクトもその流れの中に(このころは)あったはず。そもそも代数的エフェクトの代数とは、操作が(0だか1こ以上)あって、それらに等式制約を課す。それを満たすようなモデルを持つのが代数 (algebra) である、みたいな世界だっと思う。群とか環は代数だけど、体は代数じゃないみたいな話を聞いたことがある。そういうのりの代数として、エフェクトを表現したらモナドの合成みたいなことを考えるときに幸せだ、という主張がことの発端だった気がする。

ここまでは数学とかモデル理論?とかの話によっていて、あまりプログラミング言語っぽい雰囲気がしない。ハンドラとか継続が入ってきた経緯はしらないが、多分、プログラミング言語に代数的エフェクトを入れるにあたって、モナドのbindやreturnみたいなものを定義するように、エフェクトに意味を与える仕組みとしてハンドラが考えられたんじゃないかと思う。このあたりは論文をまじめに読めば分かるはず。これが確か2014年くらいのこと。

2000年くらいだったかから考えられていたエフェクトシステムとの相性に目をつけたからか知らないが、「代数的エフェクトとハンドラ」を取り入れた言語が2014年ころに登場し始める。2017年くらいにでてくる印象がある。 EffやKokaはこのへんな気がする。このあたりで、エフェクトが代数的であることはとくに気にされなくなっていき、エフェクトシステムと例外ハンドラがうまいこと組み合わさる限定継続演算子くらいの気持ちで代数的エフェクトとそのハンドラが捉えられて、やがて代数的ではないことが気になる人々がエフェクトハンドラと呼ぶようになったのではないかと思っている。

限定継続は本当に必要だろうか。もっとやさしい概念を提供するのにとどめるのはいかがだろうか。ワンショット継続とかに限定する言語もあるが、それは正しい方向性だろうか。

僕たちがほしかったものは、local reasoningしやすいエフェクトシステムとそれで健全に管理できる意味論なんじゃないかと思う。エフェクトハンドラはその条件を満たすけど、もっと使い心地のよい意味論があるはずだと思う。

最後の文をメモしたくてこの記事を書いた。それ以外は文脈である。

Google Cloudの認証を必要とするアプリケーション開発について

Google Cloudの認証・認可はきめ細かくちゃんとしている感じがして、扱うのが難しいと感じていた。少し調べたらApplication Default Credentials with client librariesの説明を見つけて腹に落ちた。これを抑えた上で、それぞれの実行環境でどのようにcredentialをセットアップするかを見ると、プロセスの権限が半分くらいわかる。ここまでで、サービスアカウントがどのようにプロセスに付与されるかを理解できるはずだ。

次に、それぞれのサービスアカウントがどのようなリソースへのアクセスをもつか、それをどうやって設定するかを確認すれば認証・認可を自由に管理できるんじゃないかと思う。How Application Default Credentials worksが良い水先案内に見える。

このあたりは知識がないと本当に挙動がわけわからないので分かりやすいところにドキュメントを置いといたり、積極的にエラーメッセージで教えてほしい。 Google Cloudを使う上での義務教育だと感じた。

Linux (gnome) でよくやる設定

GNOMEというかLinuxというかでよくやる設定を列挙する。キーボード系が多いと思う。

以下を実行

gsettings set org.gnome.Terminal.Legacy.Keybindings:/org/gnome/terminal/legacy/keybindings/ next-tab '<Primary>Tab'
gsettings set org.gnome.Terminal.Legacy.Keybindings:/org/gnome/terminal/legacy/keybindings/ prev-tab '<Primary><Shift>Tab'

参考: https://askubuntu.com/questions/133384/keyboard-shortcut-gnome-terminal-ctrl-tab-and-ctrl-shift-tab-in-12-04

xremapxkeysnailを使うどちらもそんなに使い心地は変わらないと思う。今はxkeysnailを使っている。

課題が2つある:

  • xkeysnailuinputを必要とすること
    • sudoが必要とreadmeに書かれているのはこれが理由
  • xkeysnailをsystemdに登録すること
    • sudoつけられない/等価なことはできない?

以下のように解決する

  1. 必要な権限を自分に与える
    1. sudoなしでxkesynailを実行できるようになる
  2. systemdに登録して、ログインくらいのタイミングで有効にする

これの通りにやればよい: https://github.com/mooz/xkeysnail/issues/64#issuecomment-600380800

  1. 設定ファイルを書く https://github.com/naoyafurudono/configs/blob/main/systemd/user/xkeysnail.service
  2. ~/.config/systemd/user/におく
    1. バイナリをおく: 上の例では~/.local/binにおいてあることを想定している
  3. systemdに登録する: systemctl --user enable --now xkeynail
    1. enableで登録、--nowで今実行

もしうごかなかったらjounalctl -rでsystemdのログをみる。

https://github.com/naoyafurudono/configs/blob/main/mozc/ibus_config.textproto~/.config/mozc/におく。

参考: https://blog.nfurudono.com/posts/mozc-default-engine/

2023-02-27

Gentooが入った。基本的に待っているだけではあるのだけど、それがなかなか大変。パッケージを一瞬でインストールできる幸せに気がついた。割とすぐにUbuntuに戻るかもしれない。今はnodejsとtexliveをコンパイルしている。未だかつてなくCPUが仕事をしている。かわいそうに。とはいえportageに触れるのはなかなか楽しい。

mozcの設定はこのブログのメモに助けられた。過去の自分に感謝。

(追伸) Gentooを入れるためにssdを付け替えたのだけど、そのときにじゃまで外したCPUクーラーがうまくはまっていなかった。このPCはまともな冷却なしでもろもろをビルドしていたのだ。本当にかわいそうなことをしてしまった。ずっとうるさかったのでさっさと気がつくべきだった。今はこれまでがうそのように静か。

2023-02-26

Golangをすごく久しぶりに書いた結果、構造体の要素がコピーされることを忘れておりデバッグに時間を溶かした(コピーした構造体のフィールドを書き換えて、コピー元に変化が起きると思い込んでいた)。最近はPythonやTSか、関数型言語ばかり書いていて、Cっぽい構造体の扱いをしていなかったので忘れてしまっていたのだろう。忘れていたけど、CやGoのように構造体の要素をコピーする意味論は好き。直感的というか、明示的にポインタを表現することが好きなのだろうか。

PCの構成メモ

PCを自作して使っているのだけど、どんなパーツを使ったか忘れて後でなにか買い足すときに互換性があるかわからなくてこまる。今回調査してメモしておく。

Ubuntuを入れて使っているけど特に不満はない。不満はないのだが、この間Gentooのことを教えてもらって面白そうなので使ってみようと思っている。

余談だが、このPCは親に就活で必要だろうからといって渡してもらったお金で組んだのだった。自由に使えと言ってもらったが、きっとスーツとかを想定していたのだろう。幸いなことにスーツは買わずに済んでいるので(兄のお下がりがまだ着れる)、ずっと気になっていた自作PCに手を出すのに使ったのだった。少し後ろめたい気持ちがないでもなかったが、Linuxとか触るの楽しいことが分かって仕事でもこの手のものを触りたいと思えたし、面接の話のネタにもなったので結果オーライだろう。学校とか本で学ぶOS周りの技術をそのまま試せるのが僕には嬉しかったのだろう。Windowsでは厳しいし、Macもどこか違っているので。