Zennを試しに使って見ています。最新の記事はZennを参照してください。

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は数値が変換できるか判定するやつぽい。

Neovimにlazyを入れてプラグイン管理することにした

これまで特にプラグインを使っていなかった(それくらいで済む用途にしか使っていなかった)のだけど、カラースキームを指定したくなったのでプラグインマネージャを入れた。

どれども大差ないといろんな記事で言われていたので名前とかが気に入ったlazyを使うことに。

https://github.com/naoyafurudono/dotfiles/blob/add-lazy/nvim/init.lua#L17-L55 で設定している。

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, 呼ばれる側の責務にdicisionという名前をここではつけている。

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

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

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

あとここには書かなかったけど、enforcementとかdicisionとかの話の後に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って人間が作ってるんだなと感じる。

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

2024年4月での振り返り

4月が終わるので(?)最近のことを振り返る。

1月に配属されて、2月開発にプロジェクトが始まってからずっとLOLIPOP! for gamersを作り続けている。今まで本で読んだようないろんな苦しみを体感できて楽しい。プログラミングに関することや、設計とか技術選定、開発体制や組織、チームとか締切とか工数みたいな大変さを一通り味わったと思う。

自分がうまくできなかった・できた経験や周りの人が自分より上手くやったりあるいは自分が彼らにアドバイスする経験を積めた。修行を積んでいる感じがする。

修行が必要なのはそれはそうと思うのだが、戦略的に仕事したいなとも思う。たとえば1ヶ月後は今の倍のスピードで開発できるようにしたいがどうすれば良いだろう、とか考えて立案して実行するとか。

学生の頃に感じていたプログラミングの課題が感覚としてわからない気持ちはだいぶ薄くなってきた。そろそろ腰を据えて課題を整理して解決を図る余裕が欲しくなってくる(自分で作るぞ)。

Webアプリケーションを作っている。知らない人が変更をするであろうプログラムを作ることが仕事。実行環境は不安定で、このプログラムは複数のプロセスで同時に走るし、どのタイミングでプロセスが落ちるかわからない。

DBとかの外部サービスは自分たちが実装しているプログラムよりも余程信頼できるもので、一貫性とかを担保しようと思うと外部サービスに頼ることになる。もちろん我々が実装するサービスを雑に作っている訳ではないが、外部サービスを選ぶときには厳しめな基準を持っているので信頼性が違うのは当然に思う。 Webアプリケーションはたくさんの人が作っているので、そのための外部サービスが成熟しているのは当然なのかな。一方で、プログラミング言語を設計するときに使えるツールってそんなになくて、自分で実装するものが理解できて使うの楽なイメージがある(僕はにわかなので見当違いなことを言ってると思った方が良いかもだが)。

そう思うと、Webアプリケーションを作る仕事はインターネットから情報を探し出して信頼できるツールにうまく責任をなすりつつ、本当にやりたいことを見失わないようにハンドリングすることなのかと思う。そのためにモデリングとか設計とかするのかな。インターネットから欲しい情報を探し出したりそれらを組み合わせて実装するのもまあ大変。

この解釈はなんだか悲しいな。プログラムを作ることが目的になっている視点な気がする。これを悲しいと思ったのは、ぼくが思う正しい仕事が事業で利益を上げることではなく、誰も知らなかったこと・できなかったことを明らかにして誰でもできるようにすることだと捉えているからだろう。やるのが大変で、それをやったのであれば誰もできなかったことをできたのは間違い無いのだろうが、後から来る人がそれをやりたいと思うかや、やりたいと思ったとして自分の影響で簡単にできるようになったかは別の問題。事業でやる分には競合が同じ苦労を味わったりするような状況で放置することも多いはず(この辺はペパボはいい感じだと思う)。

数年前よりも良いものを早く作れるようになることがWebのエンジニアリングでは求められていて、そのためには知見を集めつつそれでも叶わない課題を見つけてそれを解決する手法を提案・実装・共有することが必要なのだろう。その中でそれまでの仮説を否定するものがあると、パラダイムシフトが起きて自分も含めてみんなで楽しくなれるのだろう。仮説を否定するためには流れに乗るのではなくて既存の目的や仮説を理解した上で、自分が先端の課題に向き合って試行錯誤して課題解決することが必要なはず。そうやって狙ってやった提案がハマると気持ち良いだろうから、それをふんわりとしたエンジニアリングの目標にするのは一つの手だと思う。

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なんだ!みたいなことを何処かで言っていた気もするし、誰しも思うことなんだろう。

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

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

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

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

プラットフォーマの良心

プラットフォーマとして、つまり場を提供してその場を幸せな環境として維持するものとしてリーダーシップを発揮することは綺麗なことだと思う。

gmailやyahoo mailがメール送信者のガイドラインを発表して、それを元に受信者・送信者双方にとっての利益を追求したことは記憶に新しい(少なくとも僕にとっては)。風当たりのあることだし、プラットフォーマにとって利益のあるものではない(よね多分)が、それを主導したメールのプラットフォーマたちは綺麗なことをやったと思う。

僕はビジネスとか損得勘定のことはわからないのだけど、あれは間違いなく正しいことだと思う。例えば、あの声明を立案して推進して実行したのが僕だとして(そんなことはまず無理だけど、その責任を僕が負ったとして)、仮にそれが原因で責任を追及され非難を浴びたら、その非難を挙げた人々を憐れむ自信がある。憧れてしまう。ああいうことができる力やセンス・良心をもったプラットフォーマになりたいと思う。自分の良心に従って人の幸せを実現した彼らを尊敬するし、そうなりたいと思う。

ぼくの母校である大和小学校の校歌は「強く、正しく、美しく」を掲げていた。今回掲げた、憧れるプラットフォーマは強さと正しさを持っていた。では美しさはどこにあるだろうか。

RFC8058には美しさを感じた。実際的かつシンプルで、僕の尊敬するエンジニアが美しいと言ってくれそうな哲学を持っているように感じた。ああいう美しさをお前も表現しろよな!というのを校歌は僕に要求しているのだろうか。

あと僕がGAFAに目指すなら絶対Googleに行きます。Googleは美しさと正しさを持っていると思う。ブランディングが大きいのだろうけど。

話が二転三転したけど、正しさや美しさを体現できるだけの強さを手に入れたいと思っています。また、その強さを手に入れる過程で追い求めていた正しさと美しさを忘れずに、むしろよりしっかりしたものにしたいと思う。

推し言語機能 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-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);
    }
  }
}

ベストバイ

酔っていて興が乗ったので「しばらくの間で買って良かったもの」N選をやります!!

本棚を買いました!横に長いタイプの本棚で、高さは太ももくらいです。二段だけで、本棚の上にも物を置けるような感じ。幅は120cmくらい(ところでセンチって微妙ですよね)で、横に長めの本棚です。 2cmくらいの厚さのベニヤ?でできていて、それなりに丈夫そうな雰囲気があります。 minneで買いました。一階にプログラミング言語のほんを置いて、2階にそのほかの本を置いています。本棚にある本の中でのおすすめは、『数学の基礎 集合・数・位相』です。これを読むと実数の作り方がわかって感動できます。また、眠れない夜にぴったりで、楽しめるときには存分に興奮できるし、そうでもないときには眠くなります。ホテルに聖書の代わりにこれを置いたらいいのにと思ってます。

M2 macbook airを買いました!これはいい物です。ノートパソコンのいいところは椅子に座らないでも使えるところです。仕事で疲れた体でも、ノートパソコンならさわれます。退勤後もキーボードを触る幸せを感じられるのはノートパソコンのおかげです。ところで酔った状態でキーボードを叩いていると、タイプミスが頻発します。タイピングに頭のリソースを使っているようです。 m2 macbook airはキーボードがいい感じだし、性能もいいのがいいところです。メモリは16GBにしています。細かいことはよくわからないので、考えないことにします。

キリがない気がするので個数制限を設けましょう。 N=5にします。あと3個。

ルンバを買いました。ルンバのいいところは、掃除でstuckしている光景がちょっとかわいそうなところです。彼は頑張って掃除してくれるのですが、たまに段差に座礁して身動きが取れなくなります。その姿は少し傷ましくて、それを避けるための掃除をしないとなという気持ちにさせてくれます。一人暮らしの家に優しさを発揮する機会を与えてくれるのがルンバのいいところなんじゃないかと思います。こういうふうに書くと彼はポンコツみたいな表現になってしまいますが、彼は僕より掃除が上手いです。頼りにしてるよ。

会社に入るよりかは前に、echo dotを買いました。 echo dot自体がもたらす利益よりも、そのインターフェイスがあることを前提とした環境構築への影響がノミネートの理由です。 echo dotのおかげでルンバやswitch botを導入できました。音楽も流せるしアラームも設定できる。

5月の頭にリュックサックを買いました。新宿の駅の近くの百貨店で見つけて、ゴールデンウィークいっぱい悩んでから購入に至りました。出勤も旅行もオリエンテーリングの遠征も全てこのリュックと一緒です。ちょっと小さいけど必要十分な機能提供してくれている感じが好き。

いかがだったでしょうか? この記事ではベスタバイ5選を紹介しました! この記事が皆さんの参考になれば幸いです。よかったらチャンネル登録と高評価、よろしくお願いします。

<!-- エンディングテーマが流れる -->

ブログを分割したい

このブログは日記も開発っぽいことも両方書いてある。これらを別のサイトに分けようと考えている。

  • 開発の記事が日記に埋もれるのが嫌だから
  • 今の環境だと開発の記事を書きにくい・表現しにくいから
    • コードや数式を書きにくい・表示が微妙など
    • madoko使いたい

そのための準備としてnetlifyを試しに使ってみた。このブログのリポジトリを登録したら、特に細かい設定をしないでもシュッとデプロイできてしまった。すごい。DNSとかSSLとかの設定を試していく。

ところでデプロイの設定は何もわからないのでチュートリアルが必要。madoko使うなら設定ちゃんとしないとだろうし。

契約と型検査

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

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

契約 (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コンテナでセットアップスクリプトを動かして、正常に終了するかを確認するテストを書いた。

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

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

arm64のメモ

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

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

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

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

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

2023-05振り返り

月末なので気まぐれに今月を振り返る。

エンジニア研修が始まった。ビジネスっぽい話も面白かったけど、エンジニアっぽい話とは違う面白さだった。プログラムに関して議論するのはやはり楽しい。

蓼科と山口にいったし、日本ダービーにも行った。だいぶ遊んだ気がする。静かな週末は一回くらいだったかも知れない。読みたい本が溜まっている。

走る機会を減らして筋トレを増やしている。体重計に乗っていないので成果の程は定かでないが、体感筋肉が増えた気がする。たまに13km走るとしんどい。

  • 親に初任給で何買うか決めてない
    • 個人的に気になっている家電を送りつけて、レビューをもらう案が今のところ最有力
      • いいトースター
      • ルンバ
    • その他の案
      • 置き時計・壁時計
      • ケーキ(一緒に会えれば)
  • いらないものを処分したい
  • 本棚がほしい
    • これは6月の課題にしよう
    • いい感じの本棚を選んで、それに見合った量の本だけ残してあとは処分する
  • オリエン大会の運営2つに参加することになった。どちらもまだ、そんなに動いていないけど放置はできない感じ。楽しみ。

すべて6/30 9:00が締め切り。来月は振り返りと次の目標策定をする。それは6/30 23:00締め切り。

  • 親に初任給でなにか贈る
    • 喜んでもらえるもの
    • ついでに僕も楽しいもの
    • 兄に酒でも贈るか
  • 部屋をいい感じにする: 読書と衣類を最適化する
    • 本棚を一つ買う
    • 本と服を処分する
    • 夏っぽい服を買う

  • クラウド・インフラの理解を深める
    • ラズパイ買ったのでそれで遊ぼう
    • ネットワークとOSをいじる
  • 仕事の議論についていけるようになる

  • 月間120km走る
    • 2日あたり8km
  • 自炊写真をインスタする
    • 食器がネック
  • 運営で友達を増やす
    • 試走で運営に関係ない話をするひとが増えるとよい
  • PC環境をなにかしら改善する
    • 開発環境系がいいな。キーボードショートカットとかウィンドウマネージャもあり

alcoholタグの運用

ブログの"alcohol"タグをいつからか使い始めた。当初はお酒に関する記事につけるタグのつもりで使い始めたのだが、この度アルコールが入った状態で書いた記事にもつけることにした。

この記事の"alcohol"タグはどういう意味なのかはクイズにしたらおもしろいだろうか(こういう書き方をすると、お酒が入っているとばれるのだろう)。

学生生活の振り返り(技術)

昨日学籍を失って、こんど入社式に出る。学校に入ったり研究室に所属したり、就職したりするときには自分が抱える課題と向き合い、自分の幸せについて考えて進路を決めてきた。これから先はそういう機会が来たことに気が付きにくくなるだろう。

高校生のころ、大学にはいることは周りの雰囲気からして僕の意思が入る余地はなく決まっていた。そのなかで進路を悩んだ。理学と工学のどちらかにしようとは早く決めたのだが、そこからの踏ん切りがなかなかつかず、出願するときにエイヤと決めた。学問自体の面白さは理学に惹かれていたけど、就職とかキャリア的なことを考えると工学がよいのではないだろうかみたいな悩みかただったと思う。結局目先の面白さを優先して理学を選んだのだった。就職はなんとかなるだろうと踏んでいたし、学問の選択でなんとかならないのなら、自分は他の要因でなんとかならない人間であるはずなので、学問の選択でキャリアが狂うことを心配するのは無駄だと結論づけた。

高校生のころは化学が好きで、特に化学反応に惹かれていた。水素と酸素を一緒にしてエネルギーをかけると十中八九水に落ちるのが不思議だと感じた。統計的にそうなるのは受け入れらたのだが、反応の過程でなにが起きているのかがとても気になったし、この好奇心は普遍的なもので、すでに賢いひとが調べているはずなので大学で勉強したいと思っていた。大学で蓋をあけると、学部でやった化学や研究室で行われている研究は思ったよりもぼくにとって腑に落ちない考え方をしていて、そのなかで勉強してもぼくはあまり幸せになれないだろうと感じた。

では腑に落ちるのはどういうときだろうか、みたいなことが気になりだした。これは学部一年の後期くらいのこと。論理学とか心理学とか数学とかに答えがあるかもしれないと思いはじめて、論理学と数学を扱う数理計算科学系に所属することにした。所属のためにはある程度の成績が必要だったのだが、勉強が楽しかったのと、なにより友達に恵まれたおかげで、無事に所属できた。

数理計算の学部の講義(2018年4月から2020年3月)はどれも大体おもしろかった。集合論とか解析の話をしっかり数学者に教えていただけたのは幸せだったと思う。議論を正面からできる相手も複数人いてだいぶ恵まれていた。

学部では、数学の基礎や応用数学の入門や基礎(ORや確率・統計など)、計算機科学を学んだ。どれも専門的な議論はできるほどにはならないが、その手の書籍が怖くない程度には身につけることができた。数理論理学の講義も取り、不完全性定理の証明を追うなどした。これらの学問は現象を表現する数理的な手段を追求するものとしての側面をもち、当初の想定以上にぼくがかかえていた「腑に落ちること」の解釈(?)に影響を与えてくれた。

心理学とか小説とか、数理的とは限らない本を読むこともしていた。そういう本を本で、自分の中で巡らせていた理解を答え合わせしていたのだと思う。なんやかんやあって理解することとか腑に落ちること自体にはある程度の安定した解釈を持てて、実用上困らなくなった。

ここまでが学部3年のこと。学部3年の後期に研究室に体験所属みたいなことをして、輪講みたいなことをする機会が会った。そこで型理論に入門した。それまではプログラミングが好きじゃなかったのだが、プログラミング言語はおもしろいと思うようになった。研究室の先生方がとくに魅力的なことを決め手にして、所属を決めた。

プログラミング言語の理論の勉強、研究はおもしろかった(2020年10月から、研プロを含む)。論文や教科書はいままでで一番熱心に読んだし、人と議論して楽しいと感じた。研究でも小さなものではあるが世の中の知見を広げた感覚は得られた。この感覚は個人として、とても大切なものだと思っている。数学のゾクゾクする定理の証明を理解したときの感覚を、だれから教えられるわけでもなくて、自分で考えて得られたことが嬉しかったし、それを人に共有できる喜びもあった。

プログラミング言語についていると、嫌でもプログラミングのことを考える。この型システムを持つ言語は使いやすいだろうか、どういうふうにプログラミングがかわるだろうか、など。残念ながら僕にはプログラミングの経験があまりなく、その思考の幅が広がらなかった。このままではだいぶ人生損すると思ってプログラミングをがんがんやりたいと思うようになった。修士1年のころにはこの思いはそれなりに持っていた。

はじめの頃はどこから手を出していいかわからずだいぶ迷走した。コンパイラ最適化の実装やインターンへの参加もして、ある程度開発をしたが世の中で行われているプログラミングとの乖離を感じていた。

転機になったのが、mepayでtentenさんが主催して開催されたGo関連の短期インターン(2022年3月)だった。そこでGo言語に触れたことも大きかったし、なによりエンジニアやそれを志向する学生とコミュニケーションを取れたことでいわゆるソフトウェアエンジニアがどんなことをしていて、彼らがするプログラミングを垣間見た。その後友達の紹介でキャディでのアルバイトをはじめて、プログラミングの幅がさらに広がった。

今のぼくの関心事は上手に楽しくプログラミングをすることにある。きれいに書くとか、メンテナンスできるとか、思ったことを自然に表現できるとか、どういう考え方がいい、みたいなことに興味がある。これからはプログラミングの幅をもって広げつつ、上手に表現することを直近では追求したい。さらにそのためのプログラミング言語や環境などについても考えて、形にできたら楽しそうだと思う。

腑に落ちて、自由自在に扱えるようになると楽しいと僕は感じるのだろう。そのための過程にも楽しさを感じるように思う。

GitHub Actionsの実行結果をDiscordに通知

このブログはGitHub Actionsでデプロイしているのだが、ここしばらくデプロイに失敗したことに気が付かずに放置してしまっていた。

Actions Status Discordというアクションをデプロイの最後に叩くことで、いい感じの通知をDiscordにWebhook経由で送れる。

こんな感じに実行条件を指定しないと、デプロイが失敗したときに通知が実行されないことに注意。デフォルトでは if ${{ success() }} が指定されたものとみなされるとのこと。

Hugoのマークダウン処理をカスタム

HTML文書のheading要素の周りにはidがついていてほしいし、それへのリンクは手軽にコピーできて欲しい。その点Googleのドキュメントはとても好き。人に文書コンテンツを渡すときにこちらの意図がURLで表現できるし、そのリンクを踏んだ側も見るべき箇所にスムーズにたどり着ける。このブログを書くのに使っているHugoでも同じことをした。

このコミットのように、headingのHTMLへの変換を定義する

示したコミットではaタグで囲むだけでなく、heading levelを増やしている。 Hugoではタイトルをh1にするにもかかわらず、マークダウンの#h1にする。そのせいで、本文を書くときは##から始める必要があって気持ち悪い。この気持ち悪さを解消するために、マークダウンのheading level+1をhtmlのheading levelとしている。

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

使い勝手のよいウィンドウマネージャが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/

軽いドキュメントを印刷するためのツールが欲しい

さくっと書いた文書を印刷したいことがある。ビジネスライクな手紙とか、その日のTODOリストなんかがそうだ。 LaTeXやMadokoを使うのは大げさな感じがして、諦めて手書きするか、Google Docsで済ませることが多い。とはいえ手紙の書き方を毎回調べたり、フォーマットを調整したり、PCで打ち込んだデータを手書きするのはつらい。

テンプレートと変数の宣言、UIの調整と文書のまともな処理をして印刷(A4)できるものが欲しい。A4のPDFに変換できればよい。それぞれの文書のソースはマークダウンのちょっとした拡張くらいの文法で書きたい。エディタで編集したいのでGoogle Docsとかはなしで。テンプレートの作成方法はテキストにこだわらない。いじりやすいと嬉しい。

問題が以下のように分割できるはず。

  • テンプレート作成
  • ソースの文法
    • Markdown拡張
  • テンプレートへのソースの埋め込み
  • PDFへの変換

HTML/CSSやlatexを勉強してMadokoでくっつければなんとかできる気はするが、大変そうだしあまり楽しくない。文書作成ツールを自作するか?とりあえず、HTML/CSSで手紙を表現できるようになるところから始めるのが良さそうか。自作マークダウン拡張(というより処理系)はまれに欲しくなるのでやっても良いだろう。Hugoもそんなに快適じゃないし、Madokoはいじりにくいので。

  • 自分でコードをいじりやすいこと
  • メタな文書処理がしやすいこと
    • 識別子や文書の変数への束縛、正規表現やCFGベースのテキスト置換など
    • この辺はMadokoが強い
  • そういえばMadokoはPDFの生成でHTMLを使わずにlatexを使っていたな…

ここまで来て、Paper CSSという良さげなリポジトリを見つけた。 Qitaでこのライブラリを使って帳票を作る例が解説されている

まだそれぞれをあまり読んでいないのであとで確認する。このあたりの技術に合うように出力することにして、それを生成するマークダウンエンジンを用意すればok?

やはりしんどい気がする。GUIでなんとかする方法を探すのが楽だろうか。あらためて考えよう。

Google Docs APIを利用するのはどうだろうか。 APIはあまりリッチではなくて、create, update, pullができるのみ。文書構造の編集はJSONをごりごりいじることで実現する。JSONスキーマが定義されているので、これを見ながら必要な変換を定義すればよい。

テンプレートをGoogle DocsのGUIで作成して、それをAPIでpullした上で、コンテンツを注入したドキュメントをcreateすれば、やりたかったことを実現できそうだ。テンプレートの置き換え箇所の指定方法はどうするのが良いだろうか。

(開始記号)名前(引数リスト)(終了記号)を文書に埋め込む。これがひとかたまりの文書オブジェクトとして認識されることが必要。また、これにスタイリングを適用した場合、埋め込み後もそのスタイリングが保たれるようにする。引数に他の名前が出現しうることに注意する

テンプレに埋め込むテキストを定義する側では、埋め込み内容の定義リストを作成する:

def name(args: list[str]) -> str:
  return f"""\
ここに置換後の文字列を書く。
ここにも埋め込みの出現を許す。
"""

埋め込み処理では、JSON形式のテンプレのすべてのテキストについてマクロ展開をすればよい。

リブロワークスからCSSで組版するノウハウをまとめた本が5/12に出るらしい。途中までをオフィシャルが公開している。 Vivliostyleというソフトを使って、MarkdownとCSSで書籍を作るらしい。その手のソフトは他にもあるようで、比較サイトを先の書籍で紹介されていた。

こう思うとmadokoはだいぶ良いものだなと感じる。適用範囲を限定した置換とlatexはなかなか強い。 latexとcssを完全に理解すればmadokoが最高かもしれない。

PCの構成メモ

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

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

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

HugoでタグをつけるためのTips

車輪の再発明ではあるだろうけどメモしておく。archetypes/hugo newで生成する.mdファイルのテンプレを置くことができる。そこにありうるすべてのタグを書いておけば、記事を書くときに関係ないタグを消すことで、関連するタグを忘れずにつけることができる。

僕は以下のようにフロントマターを設定している。

---
title: "TODO"
date: {{ .Date }}
author: "Naoya Furudono"
draft: true
tags: [
    "daily"
    ,"PL"
    ,"tech"
    ,"ubuntu"
    ,"alexa"
    ,"book"
    ,"idea"
    ,"python"
    ,"tool"
]
---

好きなPL本

プログラミング言語を勉強するとイディオムとかその言語界隈でのベストプラクティスが学べて良い、みたいなことが巷でよく言われる。確かにそれはそのとおりだとぼくも感じるのだが、雑にチュートリアルをこなしたりするだけでは言語をよく学べないだろうと思うし、良い情報源を見つけることはそんなに簡単ではないと感じている。

僕がこれまで呼んだPL系の本でこれはと思ったものがいくつかあるので紹介する。 (注意: 好奇心でアフィリエイトを試しています。アマゾンへのリンクはアフィリエイトを有効にしています。)

どれも勉強になる。文化の共通部分と異なる部分があって楽しい。共通部分はプログラミング全般で大切なのだろうと思うし、異なる部分は言語の活かし方なのだろうと思う。

そういえばこういう本を読んだときと、良いPLの論文を読んだときでは感想が異なる。 PLの論文はすごいアイデアを中心として、周辺の概念がうまく回っていく様を見せつけられると感動するのだが、この手の本はもっといろんな機能や習慣がうまく組み合わさる様を見せつけてくれる。甲乙つくものではないだろう。

ちなみに、いま述べた類の感動を与えてくれたのはこれら2つがぱっと思いついた:

1つ目は背景知識がたくさんいる気がするので人にはおすすめしない。 2つめは必要な背景知識が少なめなはず。

このように整理すると、local reasoningがPL意味論で大切にしたい中心的な概念に思える。本当だろうか。

Pythonのジェネレータをyieldで書く例

Pythonのジェネレータ(というよりyield)の挙動を示す例を書いた。二分木を作るgenと、ノードを探索してその内容を表す文字列を返すジェネレータを返すconvを定義した。convがこういう風に定義できることを例で確認したかった。

from dataclasses import dataclass
import itertools
import random
from typing import Generator


@dataclass
class Tree:
    value: str


@dataclass
class Node(Tree):
    children: list[Tree]


@dataclass
class Leaf(Tree):
    pass


def gen(n):
    msg = f"{random.randbytes(1)}"
    if n <= 0:
        return Leaf(msg)
    else:
        return Node(msg, [gen(n - 1), gen(n - 2)])


def conv(t: Tree) -> Generator[str, None, None]:
    if isinstance(t, Leaf):
        yield f"Leaf: {t.value}"
    elif isinstance(t, Node):
        for i in itertools.chain(*map(conv, t.children)):
            yield i
        yield f"Node: {t.value}"


if __name__ == "__main__":
    import pprint as pp

    t = gen(3)
    pp.pprint(t)
    for i in conv(t):
        print(i)

2023年にやりたいこと

来年の抱負じゃないけど、一息ついたタイミングでやりたいことを整理しておくとどこかのタイミングで役に立つかもしれない。

今年は人生で一番プログラミングした気がする。 3月の末にmerpayで短期のインターンに参加したのを皮切りに、 4月にキャディでのアルバイトをはじめるなど、プログラミングをする機会が増えた一年だった。研究でもインタプリタをゴリゴリ書いてみるなどした。最近は講義で簡単な定理証明支援系の実装に挑戦している。変わり種としては、IBMの方々が講師として開催されているJVM関連のコンパイラ/VMの講義にも参加している。

型システムつきの言語ばかり触っていては視野が狭くなってつまらないだろうと思って意識的にPythonやJS/TS、Racketを使ってみるようにしていた。ライブラリをつなぎ合わせるくらいなら不便がなくてよいのだが、ある程度込み入ってきて、一つのデータ構造を複数のコンポーネントから使うようになると型システムが欲しくなる。クラスや契約である程度カバーできる面はあるが、型システムにまもってもらう安心感は得られない。抽象化をしきれないというか、しても心のどこかで実装を意識しながらデバッグしたりすることになるし、そもそも抽象化を書きにくかったりする。

なんにしてもプログラムをゴリゴリ書く能力はついたと思う。今年はとにかく動くものを作る系のコーディング技術を身につけた一年だった。年の最後にPythonで言語処理系を実装するとき、クラスを使わずdictとlistだけでどこまでいけるかやってみたのだが、型システム(依存型を含むので、いわゆるeval的な処理も必要)の実装で力尽きた。その経験からやはり型は必要だと思い直したのであった。 dataclassを使って一日かけて書き直したらまともに実装できたので感動している。もはやC++とかで書いた方がいい気がするが。

3月くらいまではあまり実装をする人ではなかった。実装に手を出し始めたのは最近の話。プログラミング言語の理論(型システムとか抽象機械とか意味論とか)ばかり見ていて論文やドキュメントを読むのが何より楽しいと思う人だった。論文を書く中で言語の使い手の気持ちがあまりわからなくなったり、論文を読んでいても、プログラマの幸せにつながる未来を想像できない研究をみたりした。

プログラミング言語や処理系はプログラムや表現したい対象があって初めて良し悪しが見えてくるものだと思う。そういうプログラミング言語の研究の動機の部分をもっと知りたくて実装に手を出してみた。結果は上々で、今の僕は去年よりもプログラミング言語やプログラミングのことが好きな自信があるし、そういう話をして楽しい人間に近づけたと思う。丸くなってしまいはしたかもしれないが。

インフラっぽいことを型システムやコンパイラの表現力/処理でまっとうに行いやすいようなしくみを考えて、実現に向けた方針くらいを打ち立てたい。インフラの勉強とRust, Scala, Haskell(適切なものを一つ)の業務で自在に使えるくらいの知識が必要だろう。

DSLやconfigでインフラ/ミドルウェアを構成するのが最近流行っているようだが、そういうのを勉強するのはつらい。あまり長持ちする技術ではないだろうし、知識としての深みも出にくそうな印象をうける。ライブラリとして提供してもらって他の便利機能はプログラミング言語に移譲するのがよいだろう。適切な言語であれば、ライブラリ特有の問題を言語の機能で扱いやすくできるはずだ。僕がやりたいのは、そのやさしく扱いやすくする部分。

その他にも全然知らない世界(技術的)をみたいし、文化的な面でも知らないところに飛び込むつもりだ。今年はそういうことをしやすい一年だと思う。楽しい一年になりそうだ。

まずは修士の研究をやろう。

git grep で検索して置換

git grepは便利なのだが、置換の機能がない。ぐぐるとsedと組み合わせて置換する方法がたくさんでてくる。スクリプトファイルに書き込んだのでメモしておく。

  • POSIX準拠の正規表現を使える。
  • 第2引数(置換後の文字列)ではマッチグループを参照できる。("\1にマッチした"のように書く)
#!/bin/bash

if [ $# -ne 2 ]; then
  echo "Arity mismatch. want: 2, actual: $#" 1>&2
  exit 1
fi

git grep -lE "$1" | xargs sed -i -E "s/$1/$2/g"

個人メモ:~/.local/binに置いてある。

読書メモ: Go言語による並行処理

ちゃんと読もうと思ったときのための自分向けのメモ。

  • プログラミング技法を学びたいなら3,4章
  • Golangの復習をしたいなら3章
  • Golangのランタイムを覗きたいなら6章を読むと良さそう。

ざっと見た感じ、だいぶ信用して良さそうに思える。積極的に読んでいこう。

Hugoでページネーションを無効化

このコミット でトップページのページネーションを無効化した。やったことは以下の通り。

  • .Pagenatorを使わないようにする
  • 対象のポストを全件表示するようにする
  • ページネータを消す

最初の一つがpage/2/みたいなページ生成を抑止して、次の2つがトップページの見た目を変える。ページネータを消せば自然に.Pagenatorを使わないようになるはずだ。

Church Encoded List in JS

Charch encodingで書いたリストの動くものがほしかったので書き下した。isnilを実現するためにコンスをisnilcarcdrからなるペアとして表現した。

// cons list in Church encoding

const tr = t => f => t;
const fl = t => f => f;
const ite = c => t => f => c(t)(f);

const pair = f => s => pi => pi(f)(s);
const fst = p => p(f=>s=>f);
const snd = p => p(f=>s=>s);

const nil = pair(tr)(0);
const cons = x => xs => pair(fl)(pair(x)(xs));
const car = l => fst(snd(l));
const cdr = l => snd(snd(l));
const isnil = l => fst(l);

const fix = f => (x => f(y => x(x)(y)))((x => f(y => x(x)(y))));

const foldr = fix(folD => f => init => l => (ite (isnil(l)) (() => init) (() => f(car(l))(folD(f)(b)(cdr(l)))) ) ());

const len = l => foldr(elm => b => 1+b)(0)(l);
const len2 = fix(leN => l => (ite(isnil(l))( () => 0)( () => 1 + leN(cdr(l)) ))() );

const lst = cons(1)(cons(2)(nil));
len(lst); // -> 2

Mozc のデフォルトエンジンを設定する方法 2022 Oct

設定ファイルを書き換えるとmozcのデフォルトを日本語入力にできる。 Ubuntu22.10でしか試していないことに注意。

$ cat ~/.config/mozc/ibus_config.textproto
engines {
  name : "mozc-jp"
  longname : "Mozc"
  layout : "default"
}
active_on_launch: True

Distributor ID:	Ubuntu
Description:	Ubuntu 22.10
Release:	22.10
Codename:	kinetic

Ubuntuで日本語をmozc、ローマ字を元から入っているUSのなにかで入力している。このやり方のありがちな課題として、mozcのデフォルトが直接入力になっていることがある。安直に対処するなら、ubuntuにログインするたびGUIでmozcの切り替えを行えばよいが、毎回操作するのは面倒だ。デフォルトで日本語モードにすれば良くて、その実現方法を冒頭に載せた。

今年追加されたデフォルトエンジンを指定するオプションを用いて日本語入力をデフォルトにしている。環境によっては設定ファイルを置くべきディレクトリが違うとか、既存の設定とマージする必要があるみたいなことはあるかもしれない。

aptのドキュメント整理

雰囲気でaptを使っていて、リポジトリ周りの扱いに困ることがある。この手のツールは一生使うだろうから勉強して損はないだろう。この記事には読んだドキュメントと一言コメントを残しておく。

  • apt(man)
    • 意図的に情報を絞っているとのこと。数分で読み切れる。
    • ざっくりと概念をつかめた気がする。
    • 次の課題はリポジトリ
  • sources.list(man)
    • リポジトリのリスト。aptが参照/手入れする。こういう名前のファイルがあるし、似たようなディレクトリもある。それらの総称としてsources.listと呼んでいる節がありそう。
    • くじらにっき++がやさしい

これでざっくりわかった。次にセキュリティのことを調べる。というのも、aptが失敗するのはセキュリティ周りの設定がうまくいっていないことが原因なことが多く感じるから。

  • apt-secure(man)
    • ここで、apt-keyで鍵を登録すると書かれているが、この方法は非推奨になっている(apt-key)。
      • gihyoに解説記事がある
      • 代替ツールはなくて、manでは直接ディレクトリに鍵ファイルを放り込むことが推奨されている。

トラブルが起こったらこのあたりをいじればよさそうだ。 apt経由でインストールする際のおまじないの意味もわかるようになった。確かSlackをaptでインストールした際にリポジトリや鍵を追加したはずなのだが、当時はよくわかっていなかった。

エフェクトハンドラと依存性注入の関係について調べる (文献調査)

僕はエフェクトハンドラについてそれなりに理解していると思う。一方で依存性注入についてはふわっとしか知らない。

そんな状態ではあるけれど、エフェクトハンドラは関数型プログラミングで依存性注入するための素直な表現に使えるのではないかと感じている。

このポストではネットやアカデミアでどんな議論がありそうかをざっと眺める。それらの議論の細かいところとか、考察とかは明日以降の意識がはっきりしているときに扱う。

同じことを思ったひとはいるようで、検索すると2017年のブログ記事がヒットした。Hacker Newsのスレchowellskybernetikos が同様の議論をしている。これは2019年のこと。

Jonathan Brachthauser と Daan Leijenの論文

エフェクトハンドラの使い方を調べるなら、モナドの使い方を調べるほうが早い気はする。そう思って調べると良さげなQiitaの記事をみつけた

まだ列挙したドキュメントをあまり読めていないので、当初知りたかった関係についてはまだよくわかっていない。でも何かしら関係あることは間違いなさそうだし、僕の直感もそんなに的外れではなさそうに思える。明日起きたら読む。

Linuxコマンドがわからない学部生について

学部生や教育を非難したり愚痴を言ったりするつもりはない。

情報系の学部2年生向けのプログラミングの講義でTAをやっている。 Linuxコマンド( cd とか lsgit など)を知らないとか、ディレクトリやパスなどの周辺の概念を知らない学生が少なくないように感じる。

講義で教える類のものではないのだろうけど、学生は真面目に講義を受けていればマスターできると想像して自律的に学ばないから僕が観測したような状況が生まれたのかなと想像する。というか、昔の僕がそうだった。

ああいう概念を身につけるためにはターミナルをたくさん使うのが推奨されがちな気がするが、卵・ニワトリ論争というか、エアーマンが倒せない理論と同じことが起きる気がする。なので個人的には適切な入門書を読むことを勧めたい。今回のLiniuxコマンドや周辺の概念は新しいLinuxの教科書に多くを学んだ。学部生たちにもこれを勧めてしまって大丈夫だろうか。

格安の引越し業者が良かった

今日引越しをした。 1年半くらい住んだ家から学校の近くへ引越した。動機は学校がオフラインになったから。告知から実行までが短くて困った。 2週間後から週4で対面参加せよと言われて強引だなあという気持ち。なんにせよ、この状況では学校の近くに住むのが有利なので引っ越すことにしたのだ。新居はそれなりにいい感じで、部屋も十分広いし町もよいところに感じた。不満は坂があることと、水回りの設備に伸びしろがあること。

新居の話はこれくらいにして、本題の引越し業者の話をしよう。大学入学から数えて、今回の引越しは3回目か4回目なのだが業者を手配したのは初めてだった。これまではヤマト運輸の単身パックや家財配送便?などを利用しつつ、気合や周囲の優しさで引越しをこなしてきた。たとえば洗濯機だけは業者に任せて、あとのものはリュックに入れたり現地で買うなど。他にはトラックを借りて荷物を運んだりもした。

今回業者にお願いしたのは単にコストパフォーマンスが良さそうなことに気がついたから。支払った金額は22,120円なので、軽トラを借りて友達にバイト代を払うとトントンになりそうだ。業者にお願いすれば僕は疲れない上に時間も短く済む。そして結果論ではあるが業者の方は丁寧な作業をしてくれた。ほんとにあの額で良かったのだろうかという気がしてくる。あの人たちはどれだけの報酬をもらえているのだろうか。まとめると、金銭的には同等のコストがかかり、時間的には業者に依頼するのが有利、得られる結果も依頼するほうが良さげ。依頼するしかないだろう。

特に安く抑えられたことが効いている。時期と依頼先が良かったのだろう。今回はよい行動ができたと思う。勝因は勢いとコミュニケーションだと思う。引越しのような行動は勢いがあれば8割くらい(10割がどんなか知らないが)のクオリティを達成できる気がする。ある程度の予備調査は必要だけど、勢いを殺さない範囲の調査で十分なはず。プラス2割を得ようとすると話が込み入ってくる印象だ。今回行った調査は以下の要領で行った:

  • SEO対策が効いてる系の記事で引越し業者選びの雰囲気を掴む
  • 相見積もりサイトで業者の候補を得る (5社程度)
  • 業者に直接問い合わせる (2社)

もっとよい選択はあったと思うのだけれど、話が込み入りそうだったのでこれくらいで打ち切った。これが8割の行動だと思っている。お金の使い方や事務処理を学んだ気がする。学びを活かしていこう。

Wishlist

欲しい / つくりたいソフトウェアのリスト。随時更新していく。

  • マークダウンフォーマッタ
    • 全角文字にうまく対応したり、Madokoのシンタックスを理解したりしてほしい。

Syncthingがよさげ

オープンソースのソフトウェアでSyncthing というものを知った。 LAN内のデバイスとはLAN内で、インターネットの先にいるデバイスとはインターネット越しにファイルを同期できるソフトウェアだ。予めクライアントをインストールして、ディレクトリごとに共有設定をしておくと、設定に応じてよしなにファイルを同期してくれる。

USBのような手軽さはないが、クラウドストレージ越しにファイルのやりとりをするよりは便利そうだ。 UbuntuとAndroidにクライアントを入れてつかってみているのだけど、今の所いいかんじ。学校のネットワークではP2P通信を禁止している。おそらくSyncthingも引っかかるので、学校のネットワークでは動作しないように設定しておく。

設定は公式に従ってもよいし、このブログポストも参考になる。詳しいことはArchWikiが頼りになりそう。

落し蓋

ひじきの煮物をつくったのだけど、あまり上手にできなかった。その原因をさぐりたい。

まず、ひじきの量に対して他の具材を入れすぎた。ひじきが主役っぽくなくなってしまった感じ。次に味が結構こくなってしまった。これは煮物という感覚を持たなかったことが敗因だろう。水を入れずに醤油、みりん、めんつゆだけでなんとかしようとして濃くなったの思う。途中で水を追加したけど、味は変わるわけではなく…。とはいえ、食感はおいしかった。

落し蓋をしなかったことも影響しているのだろうか。落し蓋の効果はいろいろあるようだけど、水分の蒸発を防いだり煮汁の対流を促進して味を均等にする効果があるらしい。

ひじきの煮物が苦手というより、煮物を作るための基礎的な知識が不足している気がしてきた。煮物は漸進的に調理するのが難しい気がする。スープなら具材の火の通りやすさを加味して順序を決めれば、あとは味見をしつつ味付けすることでおいしくなる。カレーとか味噌汁とかはこのあたりだと思う。肉やさかなを焼くときは、下処理と焼く面に気を使えば大体満足できるものが作れる印象がある。火が通ってなかったらそこだけ焼き直せばいいし。サラダはいわずもがなだし。これらに対して煮物は水分のコントロールが難しい。具材、調味料の水分をうまく扱わないといけないので。なんというか、すべての材料のバランスを調理前に決定しておかないといけないイメージがある。

料理のさしすせそも煮物に関するtipsなのか?

「桜井政博のゲーム作るには」がおもしろい

桜井政博のゲーム作るには というYouTubeチャンネルが面白い。ゲームの仕組みに関する知識を説明してくれる。動画のクオリティがすごいし、何より内容が面白い。

桜井さんが語っているyoutubeラジオ?があった。明日聞いてみよう。ものをつくるときに気をつけていること/ゲームクリエイター 桜井政博さん 第1回「ユーザーの身になる」 を初回として、第4回まであるようだ。一回20minちょっとくらい。

Daily Note: 腕時計の電池交換

コロナが流行ってから外出する機会が減りそれにともない腕時計をつけることも減った。今度腕時計が必要な用事があるのだけど、電池が切れていたので交換してもらってきた。 1320円かかって、15分ほどで対応してもらえた。電池を買うと500-600円くらいかかるだろうし、工具代はもっとするだろう。作業としては簡単な気がするが、この値段でやってもらえるのはありがたい話だ。この時計の電池交換を何回することになるかはわからないが、それ以外に使いみちのない工具をさがしたり、管理する手間を考えれば安いものである。良い価格設定をしてくれている感じがして、あの時計屋さんを好きになってしまったかもしれない。

Ubuntu22でスクリーンショットの保存先を変える方法

Ubuntu22でデフォルトのスクリーンショットアプリはおしゃれな見た目をしていてかっこいいのだが、保存先を変える設定が見当たらなくて困っていた。デフォルトでは ~/Picture/スクリーンショット/ に保存されるのだが、~/Desktop/ に保存されてほしいのだ。

StackExchangeに回答があった。回答によると、やはり残念ながらユーザによる設定はできないようで、シンボリックリンクを張る手法が提案されていた。僕もそれに倣ったら具合が良いのでリンクをメモしておく。

別の解決方法として、gnome-schreenshot をインストールして使う方法 もあるようだ。しっかり読んでいないのだが、やることが多くて面倒にみえる。

Todo Appを作っている話

この記事は書きかけです。

Todoアプリを作っている。就活の際に Webアプリの開発経験に関する話をするときの題材が欲しかったから。

GitHub: https://github.com/naoyafurudono/todo-app

Todoアプリを選んだのは、この手の目的でよく作られているイメージがあるから。FizzBazzみたいな? 見た目だけ整えてもつまらないだろうし、コマンドラインだけで完結するようなものならSQLで良い。作るからにはなにか自分にとって得るものやソフトウェアとして売りになる点が欲しい。

細かいことはreadmeにしっかり書いている。しっかり書いたことも頑張りの一つ。ソフトウェア設計のためのツールを勉強できた。この記事では技術を選んだ理由や学んだことを書く。

フロントはTypeScriptとReactを用いた。

JSではなくTSを使ったのは、僕が常々型がほしいと思っていて、仕事でも型を大事にしたプログラミングをしたいから。 TypeScriptはほとんど初めて書いた。型システム(といってよいのだろうか)にはクセがあるが、型システムがいてくれてよかったと思う機会が何度かあった。これが静的型付言語を用いたプログラミング体験で最良だとは思わないけど、なかなか良かった。

良かった:

  • 型としての文字列リテラル
  • ユニオン型
  • JSONとの連携

enumというか、代数的データ型っぽくオブジェクトを使える。網羅性検査やコード補完もしっかり効いて快適だった。これはTSの特権だと思うのだが、JSONとの親和性がすごく良かった。

良くない:

  • 関数の型を書きにくい
  • ライブラリ関数の型が調べにくい、使いにくい
  • 型推論の結果が欲しい型になかなかならない
  • any

これらの辛さの原因は、僕の文化への慣れと、構造的部分型と型エイリアスを多用する方針のどちらかだろう。関数の型が書きにくいのは慣れの問題だろう。Haskell基準で語っているので理想が高いのかもしれない。型が調べにくいのは、なれとJS向けドキュメントによる検索汚染が原因か。 Reactみたいなライブラリの型を調べる際にはどこをみるべきなんだろうか。

型推論は型システムの方針の問題だろう。たとえばオブジェクトを返す関数を書いたとき、気持ちとしてはすでに定義してある型エイリアスを提案してほしいのだが、実際にはレコード型みたいな形の型が推論結果としてフィードバックされる。主要な型は確かにそのレコード型みたいな型だろうからその挙動は理解できるし、まっとうだと思いはする。トップレベルの関数定義では、型アノテーションを書いてから本体を定義するのがやはり良いのだろう。書きはじめのころ型アノテーションの書き方がわからなくて、VSCodeに書いてもらおうとしたときにこの不満が出たのだったと思う。

anyは本当に良くない。型をつけたプログラムをデバッグするときは、強く型がついた前提で考えるので、any周りで前提が崩れて状況の認識に失敗する。 anyを書かないように頑張ることが必要だ。デバッグの際にはanyの可能性を忘れず、JSの気持ちで考えるのが良いのだろうか? それでは嬉しさ半減な気がするのだが。。。

不満が多くなってしまったが、TypeScriptはそれなりに好きだと思う。

Reactの他にElmを使うとか、UIはしょぼくするとかの案があった。判断の理由はReactの話に乗ってくれる人が多そうな気がしたから。個人的に最近Reactのことを調べていて、しっかり書いてみたい気持ちもあった。

関数コンポーネントで書いたし、useEffectやuseReducerもしっかり使った(つもり)。比較対処をそんなに知っているわけではないので、感想は少なめ。useEffectとかuseReducerはよくできているなと思った。うまい。

今回は複数クライアントがTodoを共有するようにした。WebSocketでサーバを通してイベントの同期をとって、同期が取らたイベントだけを反映させている。これはCroquetというメタバース環境の実装方針のアイデアを拝借した。イベント同期のためのWebSocketクライアントの管理のためにuseEffectを使い、イベント処理のためにuseReducerを用いた。

Golangを用いた。ゴルーチンとチャンネルの使い方や、構造体型の設計がセールスポイントだろうか。特に凝ったことをしたわけではなく、「これくらいは書けます」とアピールできるだろうと見込んでいる。

力尽きたのであとは宿題。

2年越しの再開をする。

サーバは特に工夫をしなかったと思う。サーバをできる限り薄くして、アプリケーションのロジックやデータ構造の定義をフロントエンドに寄せることを意識した。サーバはデータ構造のパースをして壊れていないことを一応検証したり、イベントにタイムスタンプをつけてイベントに全順序をつけることを責務として持つようにした。 Websocketで全てのクライアントと接続して、pub/subみたいなことを実装したのも頑張りどころではあったのかな。

Python のfilter関数が良い

ドキュメント を読むのが早い。

こういうことができる。

$ cat map-none.py                                                                                                                          17:34
def f(x):
    if x < 0:
        return x

for i in filter(f, map(lambda a: a-10, range(100) ) ):
    print(i)

$ python3 map-none.py                                                                                                                      17:34
-10
-9
-8
-7
-6
-5
-4
-3
-2
-1

静的型つき言語では fmaybe を返すようにして、filter の代わりに mapMaybe のような関数を使うところだ。 Python のおしいところは、filter という名前でよぶところか。

def map_maybe(fn, lst):
  filter(fn, lst)

とすれば良い話ではあるが。

こういう機能は Python に限らないような気はする。動的で関数型に関心のある言語ならこういう仕組みにするのが None のような値の扱いとして自然だろう

TODO: 他の動的言語での扱いを調べる。

  • scheme
  • CL
  • JS
  • ruby
  • julia

Reactのしくみを学ぶ

この記事は僕の勉強メモです。資料へのポインタが主なコンテンツになるかと思います。更新は随時入るでしょうし、煩わしいので追記した旨も基本的には記しません。

Reactの再描画に関するしくみを学ぶ。再計算をするかしないかの判断や、計算結果をDOMに反映するタイミング、言語としてみたときの意味論などを理解したい。

いわゆる普通のプログラミング言語のような実行モデルではない。どのような理解を詳しい人はしているのだろうか。サーバサイドレンダリングとかはどのように関係するか。 Partial evaluationだと思える?とか。

  • 日本語の記事で、このブログ記事 の著者がまじめにReactを使うために学んでその知見を発信している。
  • 上の記事からリンクしているが、Reactの開発者が(再)レンダリングについてまとめた記事 を2020年の5月に公開している。コンセプトは、「ネットに散らばったまともな説明を一箇所にまとめて理解を得られるようにする」。体系的に説明されているし、ソースを示すリンクもたくさんはられている。良いHTMLって感じ。
  • 上の記事で紹介されているように、Reactはアプリケーションの実行時にオブジェクトを管理するためのデータ構造として、Fiberを使っている。それについてまとめた記事の日本語訳がこちら。さらにそこからReact componentなどに関する公式ブログへのリンクがはられている。

学術論文は探したけど見つからなかった。最新の情報はFacebookの人が発信する情報(ReactConとか)や公式ブログでのアナウンスに頼ることになりそう。 GitHubのIsuueとかも最新か。

救急でのITシステムについて

全然詳しくないのだけれど、救急で受け入れ先の病院が見つからない問題は改善する見込みはあるのだろうか。きっと受け入れのプロトコルやデータの管理方法や、病院のキャパビリティに課題があるのだろう。プロトコルやデータ管理はITシステムをうまく構成して普及できればなんとかなるはずだ。そういうことを実現しようとする事業は既に存在するのだろうか。少なくとも今はまだ成功していないのだろう。どんなところが実現に対しての障壁になっているのだろうか。

このあたりのことを含めて医療に関する情報処理はもっとよくできるのではないだろうか。コロナでの病院の手続きとか。どういう企業が事業の実現に近いのだろう。

円周率とユークリッド空間

タイトルがこの記事の主なコンテンツなのだが、円周率はユークリッド空間に対して定まる量なのだろう、という推測と同じことを主張しているサイトの紹介をする。

その主張をしているのはこちらの裳華房のサイトだ。裳華房は大学の理系の教科書を扱う出版社。

空間で円が定義できるためには、空間に距離が入っていて欲しいだろうし直径と円周の比がすべての「円」に対して一意に定まる必要がある。

  • 円の定義はなにか
    • 距離があれば定義できるか?裳華房の主張いわく否なのだろう。
  • 円周率がwell-definedであるために必要な空間の条件はなにか

裳華房のページで紹介されている『円の数学』の1章が詳しそう。

Quoraのポストで球面での「円周率」を議論している。ポストを見るのが早いが、一応解釈をメモ。

球面上では円周率(円周と直径の比)は定まらない。一意性が無いことを示す。

大円の長さが$L$の球面上で直径$L/2$の円を書く。この円は大円なので、導出される「円周率」は$2$である。

直径$L$の円は一点(中心の対蹠点)なので「円周率」は定まらないというか、$0$というかに」なる(一点の長さの定義に依存する)。これで証明は終了。

直径を大きくすると円周は$0$と$L$の間で振動する。したがって、「円周率」は$0$に収束することがわかる。

Local File Transfer

近距離でのファイル交換はあまり洗練されていない印象がある。 Apple製品間ではAirdropが使えるし、Windowsにも似たような機能があったはずだ。でも汎用的に(たとえばOSを気にせずに)使える機能は見かけない。そういうときにはGoogle DriveやDropBoxみたいなクラウドサービスを使うか、USBメモリを使うのが一般的だろう。 Slackとかメールに頼ることもある。

インターネットに頼るのは不便だし、USBメモリが刺さらないデバイスも多い。 Blootoothを基本の通信方式として、それが使えない場合にインターネットを経由するのはいかがだろうか。

通信技術は既存手法を組み合わせるだけで良いだろう。難しいことはないだろう。一方でアプリケーションのUIには工夫が必要なのではないだろうか。

  • どこにファイルを保存するか
  • OS間でのファイルの互換性
  • 送信先の選択
  • 受信の制御

これらの選択に一般的な回答は存在するだろうか? 他のアプリに組み込む形がよいかもしれない。例えばローカルの会議で各自が自身のPCで文書や画像、図面などを編集するアプリがあったとして、参加者間でのデータ共有のために今考えているものを使う。このときUIはアプリの特性から決まるだろう。

この例に対して、スマホやPCのファイルシステムは一般的すぎて設定を決め打ちできないのではないだろうか。そうすると、ファイル共有の実行時にユーザが設定をあたえることになる。大変不便だろう。

設定ファイルの編集やディスパッチの機能をつければ楽になるか? ルールエンジンとかは大げさだろうか。

REPLと単体テスト

Pythonのことを考えていて、TDDの文脈での単体テストの意義にふと気がついた。つまり、失敗するテストを先に書いて、それを通るように実装をする、という方法論の意義に気がついた、ということだ。

PythonやHaskell、Lisp、ScalaのようなREPLがある言語で開発をするとき、僕はテストを先に書く理由が今まであまり腑に落ちていなかった。それはおそらく実装して、REPLでデータを渡せば動作確認できるからだ。

もちろんユニットテストを書いておいた方が再利用性とかを考えると良いだろうし、リファクタリングの助けになるのも分かる。ただ、コードを書き始める前にやるほどのこととは思えなくて、プログラムに対してユニットテストを書くことは、文章に対して推敲するような行為だと捉えていたのだ。コードがあっていそうなことはREPLで確認するのが手軽で、ユニットテストは重い作業というか。この重さは面倒なだけでなく、実際に開発速度を落としていたように感じる。

さらには何がほしいか明確ではない段階で単体テストを書いて、実装してみて、なんか違ったときにはたくさん変更する箇所があって、テストケースとプログラムの修正順序が逆転することもある(このプログラムは正しいので、怒っているテストを修正する、ということ)。

単体テスト as 推敲のパラダイムは割と悪くないと思っている。世間の皆さんはどう思っていて、実際的な方法はどんななんだろうか。

P.S. GoとかRustみたいな言語では単体テストは、もっとガンガン単体テストを書けば良いと思う。というのもこれらの言語にはREPLいから。単体テストを書けばmain関数に手を入れずに、比較的お手軽に小さい確認をできる。

ここまで書いて思ったのだけど、REPLで確認するレベルの動作確認をGoとかではテストで確認する必要はあるだろうか?(僕はないと思うことが多い) 型検査やlinterにかけることが気持ちとしては同等の検査? 言語が違えばプログラムの構造が質的に変わるので、細かい比較はあまり意味がないか。

結論は 「REPL ~ 静的検査」の対応がなんとなくあって、単体テストはもう少し大きめな検査? あまりスッキリしない。この辺りの考えを胸に置いて、コーディングしていれば知見が得られるかもしれない。

UNIXでのパーミッション周りでの振る舞いを調べた

systemcallを初めて書いた。パーミッションの挙動を調べるのがモチベーション。以下を実行できてほしかったのだが、sudoにそんなファイルは無いと怒られてしまう。

gcc hello.c
chmod 000 hello.c
sudo a.out

a.out をexecvで読んだところ、そこでもエラーが起きた。errno をみるとパーミッションが無いと怒られたようだ(execvを呼ぶバイナリをsudoで実行した)。

rootとして実行できていないのか、execvが認識するプロセスのユーザidがeidではなく、uidなのか…。

次の実験をした。sudo は関係ないようだ。 sudo su でrootになったあとの様子を切り取っている。

# ls -l
合計 44
---------- 1 root     root     16520  7月  8 18:22 callee
-rw-rw-r-- 1 furudono furudono   103  7月  7 15:43 callee.cpp
-rwxr-xr-x 1 root     root     16136  7月  8 18:25 caller
-rw-rw-r-- 1 furudono furudono   240  7月  8 18:25 caller.c
# cat caller.c 
#include<errno.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>

int main()
{
  char ** args = {NULL};
  char * cmd = "./callee";
  errno = 0;
  int c = execv(cmd, args);  // my first system call
  fprintf(stderr, "%s\n", strerror(errno));
  return c;
}

# ./caller 
Permission denied
# chmod 100 callee
# ./caller
hello world
# chmod 010 callee
# ./caller
hello world
# chmod 001 callee
# ./caller
hello world

カーネルかファイルシステムの都合でこうしているのだろうか。上で登場した caller.ccallee.c は GitHub で公開している:

https://github.com/naoyafurudono/test-unix-permission

Systemd

systemdでxremapを追加して、システム起動時に勝手にxremapが走るようにした。

僕の環境:

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 22.04 LTS
Release:	22.04
Codename:	jammy

xremapの作者の方が公開している設定を参考にした。

https://github.com/k0kubun/xremap/blob/6e8e1f21285ecedfa7ac88d703ad80d25a2699dd/examples/systemd/xremap.service

依存先が僕の環境では存在しないと怒られたので、default.target を指定した。この設定ファイルを ~/.config/systemd/user において、systemctl --user enable xremap を呼びだす。すると次回のsystemdが起動するときに、xremap を呼んでくれるようになる。

[Unit]
Description=xremap

[Service]
KillMode=process
ExecStart=/home/furudono/.local/bin/xremap /home/furudono/.config/xremap/xremap.conf
ExecStop=/usr/bin/killall xremap
Restart=always
Environment=DISPLAY=:0.0

[Install]
WantedBy=default.target

快適になった。

Spotify at Commit

Spotifyの再生が一瞬途切れることがある。そしてそれがシェルコマンドを叩いたタイミングと重なる。どんなカラクリなんだろうか。ただの気のせい? さっきはコミットしたときに一瞬途切れた。

Shell Commands

パーミッションのことが気になって、シェルコマンドのソースコード(C言語)を読んでいるのだけど、読んでいて楽しい。rm から読み始めたのだが errorno やコマンド引数の扱い方、トラバース (ftsを使う) の書き方で学びが得られた。 Goのエラーハンドリングの強さが分かる。

なお、パーミッションはコマンドの実装とは分離されているみたいだ。ファイル削除は unlinkat システムコールが実現していて、その前後で権限取得みたいなことは行わない。

次に chmod を読んでからシステムコールとOSの話を見にいこう。

競技プログラミング

数ヶ月前からときどきAtCoder (my account) のコンテストに参加している。就活とか関数型ではない言語でのプログラミングを意識して始めた。今回のABCで灰色を脱して茶色になったので記念に記録しておく。

はじめは入出力がなによりも難しかったし、なんなら今も悩むのはそこな気がする。次に問題文の誤読が多い。これは恥ずかしいことなので特に気をつける。慣れもあるのかもしれないが。そしてC++で書いているのに関数型な頭で取り組んで苦戦するケースもある。 forではなくて、mapとかをゴリゴリ使いたくなるのだ。今日もmapを使おうと思って検索したのだけど全然検索でヒットしなかった。まあ、mapはC++で使うべきではないことは分かる。

イテレータを使うのが面倒ですこし困る。begin()end() をいちいち書くのは嫌なものだ。Rustに移行すれば楽になるのかな。もう少し勉強してみるか。

おかげでC++には少しなれることができた。欲しい機能がないことがよくあるけど、ちょっと頭をひねればきれいに回避できることが多い。訓練だと思ってC++で続けるのはありかもしれない。 Rust欲はあるので移行はあり。

色の話をしておこう。今のパフォーマンスだと緑色の下か真ん中くらいなようだ (900-1100)。言語と環境により慣れれば多少上がるかもしれないが、大幅な違いは生じないだろう。なにより今のアルゴリズム力で人生を終えるのは寂しいので頑張りたい。脳の老化が始まるまで、そんなに時間は残ってないはず。

とりあえず朝活するかな。

Phantom Type

Phantom type (幽霊型) をチラッと学んだのでメモ。きっかけは Gentrification gone too far? affordable 2nd-class values for fun and (co-)effect を読んでいるときに出てきたこと。

静的な型を持つ言語で使うテクニックで登場する型(パラメータ)のことをphantom typeという。

-- このaがphantom type
data Com a = String

type First ()
type Second ()
type Third ()

init :: String -> Com First 
next :: Com First -> Com Second
final :: Com Second -> Com Third

init -> next -> finalの順で呼ぶことになる。それ以外の順番では型検査を通せない。線形型とかGADTとかと組み合わせるともっとリッチなことをできるだろう。セッションタイプを似たような概念として聞いたことがある。どのように関係があるのだろうか。

Jane Street のテックブログでphantom typeをYaron Minskyが紹介していた。 readonly/readwrite/immutableみたいなアクセス制御を実現する方法を例として紹介している。

コードブロックの表示

コードブロックの表示が気に入らない。設定をいじって白地にしたい。現状では黒地。というのは、ブログの背景が白だし、僕が普段白地になれているから。白地を養護する主張 に影響を受けた。実際目が楽になったような気がする。

Golang の変数キャプチャ

Golangの変数キャプチャを勉強する。

追記: 最初、変数キャプチャではなくメモリモデルを勉強しようとしていた。勉強してみて、求めている概念では無いことに気がついたのでタイトルなどを修正した。メモリモデル自体は知れて良かった。非同期処理へのコンパイラ最適化が及ぼす影響を知れる(公式サイト におせわになった)。変数キャプチャの説明はこれ を読む。3年前に僕と同じことを思ったひとがいたようだ。

クロージャをforループ内で生成してデータ構造や高階関数に渡すような処理を書いた。そこで変数の扱い(メモリモデル)でエラーを出したことがことの始まり。解決策はシンプルなのだが、イマイチしっくりこないので勉強したくなった。クロージャの変数キャプチャは言語によりけりだし、特徴が現れるように思う。 Golangがどうなっているか知るのが楽しみ。

問題のコードはこんな感じ(The Go Playground)

words := []string{"foo", "boo", "bang"}
arr := []func(){}

for i, elem := range words {
	arr = append(arr, func() {
		fmt.Printf("%d: %s\n", i, elem)
	})
}

for _, f := range arr {
	f()
}

結果は次の通り。

2: bang
2: bang
2: bang

期待していたのはこれ。

0: foo
1: boo
2: bang

こうすると期待通りの出力を得られる(The Go Playground)

words := []string{"foo", "boo", "bang"}
arr := []func(){}

for i, elem := range words {
	i := i
	elem := elem
	arr = append(arr, func() {
		fmt.Printf("%d: %s\n", i, elem)
	})
}

for _, f := range arr {
	f()
}

どういう理屈で振る舞いが変わったのだろうか?一般的な理屈が知りたい。 2つの要素がある。for文のスコープとクロージャの変数束縛だ。

言語仕様 (rangeつきfor文) によると、range 節を伴ったfor文では、宣言した変数が使い回されるらしい。

The iteration variables may be declared by the “range” clause using a form of short variable declaration (:=). In this case their types are set to the types of the respective iteration values and their scope is the block of the “for” statement; they are re-used in each iteration. If the iteration variables are declared outside the “for” statement, after execution their values will be those of the last iteration.

言語仕様 (関数リテラル) によると関数リテラル(クロージャ)は定義もとの変数を共有するとのこと。

Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.

最初の例では、forループで宣言された変数 i をすべての関数リテラルが共有した。最初のforループが終わったあとの変数 i の値は最後の繰り返しでの値になる。なのですべて 2: bang` と表示した。

2つ目の例では、forループの中で、毎回変数 i を宣言、定義した。for文が代入する変数iは、for文がイテレーションで定義したものではなく、はじめに定義したものなのでそれぞれのクロージャは影響を受けない(クロージャはfor文の各繰り返しのブロックと変数を共有するが、ブロックはすぐに終了してそれぞれのクロージャだけが変数にアクセスしうるようになる)。したがってそれぞれのクロージャは別々の値をプリントする。

すっきりした。嬉しい。

GolangのキャプチャはC++の参照キャプチャと思ってよさそうだろうか。クロージャの側は多分良いけど、変数の生存期間が違うので類推しないのが安全か。

言語の理解が進むとその言語をもっと好きになるみたいだ。

JavaScript The Good Partsを読んだ

読んだ。きっかけはTypeScriptの関数型言語好き向けの紹介ページで事前知識として要求されていたから。読んでよかったと思う。しっかりJSの知れたのではないかと(その判定はまだできないが)。とはいえまだまだ知るべきGood Partsはある気がする、というのもconstとかletが説明されていないので、情報が古いだろうと感じたから。プロトタイプの話が載っていたり、thisの動的な意味の変化のこととか、varのスコープとかを説得力をもって説明してくれたのはとてもありがたい。そういう点で読んでよかったと思う。DOMやevent handlerなど、ブラウザ周りの話を知りたい。

JSがSchemeに近い部分があることを知られて良かったと思う(Schemeのletがあればどんなに幸せだろうと思うけど)。 JSには偏見しかなかったけど、良い言語に思えてきた。TypeScriptも知りたい。DOMとevent handlerが先だが。 UIを扱うにはJSが強い?C# with Unityもあるのか? TSよりも関心はDOMやevent handlerにある。

JSのletを勉強している。情報源はMDNvarが作るスコープがレキシカルスコープではないことは既に知っている。letはマシだが、SchemeとかOCamlで期待するような感じにはならない。MDN 一時的なデットゾーンで解説されている。 MDNでも触れられているが、letの右側(let x = <ここ>)が常にこのletが作るスコープに所属することになる。なので、常に束縛する変数と同じ名前はletの右側には出現できない(e.g., let x = x.fooは常にエラー)。その他に、同じブロックに同じ変数に対するletを複数回使えない。シャドーイングが不便。これらはすべて良くないとは思うが、そんなに大きな問題でも無い気はする。

letの仕様によって、JSでブロックを使う意味が生まれた?[^fn]switchでそれぞれのブランチのスコープを区切るためにブロックを使うテクニックが紹介されている。

[^fn]この疑問符は、他の用法はあったのだろうか、という意図。

趣味

心に残った作品を列挙しておく。順序に意図は無い。ちなみにこの記事はお酒によったときに書いたり追記したりしている。

  • 91Days
  • パリピ孔明
  • true tears
  • 天元突破グレンラガン
  • マクロスデルタ(見た時期の影響が大きいかも)
  • Banana Fish

マクロスデルタは大学受験のはじめのころ、モチベーションがない時期にみた。マクロスデルタのおかげで今の僕は自分のメンタルヘルスをある程度管理できるようになった。

民主主義みたいな事柄が気にかかっていた時期があった。

  • 人間失格
  • それから
  • 学問のすすめ
  • エヴァ(漫画、新劇場版の破以降)

心には残っていないが、スッキリさせてくれたのが文科省の「民主主義」。戦後に出版されたやつ。

これらをもとに民主主義とかパーソナリティに思いをはせたのは、社会学について講義や本で学んだ経験が影響しているだろう。もしかしたら仏教の縁とかキリスト教の愛みたいな考え方を聞いたことも影響しているかもしれない。人間性をだそう思うと、個人は責任ある(あるいは、アイデンティティのある、自我のある、人格のある)主体として振る舞う必要があって、その上で他者といい関係を築くことが今どきの社会での人のあるべきありかたである、みたいな考え方をもつようになった。

僕は昔から自我みたいなものが弱い気がしていて、そこに課題を感じつつ、一つの主体として他者と交流することに憧れたり、それに向けてもがいたりしている節がある。とはいえエヴァのゲンドウがいっていた(気がする)ように、人と人は分かりあえないみたいな考え方をぼくももっていて、だからこの努力は一生終わることが無いものなのだろう。完璧な理解みたいなものはなくて、腹に落ちた感覚があるだけだと思っている。

  • know
  • ファンタシスタドール イブ
  • うさぎと縁の本(タイトル忘れた)

アムリタからハマった。

ボカロやJPOPがすき。うたってみたもすき。とりあえず好きなアーティスト:

  • Ado
  • まふまふ
  • ろん
  • ハチ
  • dazbee
  • Vaundy
  • King Gnu

好きな曲:

  • まふまふの曲が何個か(タイトルは忘れた)
  • Snow Song Show(そらるとろん)
  • 夢花火(まふまふ)
  • リストの巡礼の年のアルバムでSpotifyで最初に流れるパート
  • Vaundyの踊り子(MVが特に好き、かっこいい)
  • King Gnuのいろいろな曲

  • フィンダーおじさん (Vtuber)
    • トークではなくて作品

心に残ったのはこれくらい?もっとありそうなものだけど。ハマったのは他にもあるが、あれは心地よかっただけなのかもしれない。本は作家で好きになることが多い。思想や文体が好きなのか?

  • 野崎まど

  • 村上春樹

  • 西尾維新

  • 森博嗣

  • 夏目漱石は少し

  • アニメはきりがない。

  • 映画やドラマはそんなに嗜まない。

  • 音楽もそんなに。リストの巡礼の年の不安定なパート(ピアノのソロ)はすごいと感じたくらい。

    • (追記)これは嘘。音楽は割と好き。書き加えておく。
  • 絵もあまり。大蔵美術館(だったか?倉敷の西洋絵画がある)でみた受胎告知はよかった。

  • 建築はかっこいいとかこの辺がイカしてる、とか思うくらい

  • 証明やプログラムは、ここでの関心とずれている?(僕の中での解像度が違うのか、立場が違う?)

  • 人間は?

これくらいにしておこう。漫画やゲームがぱっと出てこなかったのが驚きであり、また腑に落ちる感じもする。こういうことを内省するのも楽しい。きっかけは今日見たパリピ孔明がすごく良かったから。深夜テンションで書きました。

  • 2023-03-19: Big Peat

Golangでのlog

Golangの標準ライブラリにlogがある。いろいろ関数が提供されているが、機能はざっくりいうと「引数で与えたメッセージにタイムスタンプをつけて標準エラー出力に流す」みたいな感じ。ログにレベルをつけようと思うとこれでは足りない。レベルというのは、DEBUGとかERRORみたいなやつ。書くログ関数にレベルを設定した上で、グローバル変数とかで現在のレベルを設定する。プログラムを実行すると、現在のレベルで有効なログ関数だけが発火する。

Golangでこれをやろうと思うと、サードパーティを使うか自分で定義することになる。自分で定義するのも意外と悪くない。実装のほとんどは標準のlogに委譲すれば済むし使い勝手は最高。設計に注意が必要なのと、面倒なのは間違い無いのでそれが難点か。 Golangのインターフェースのおかげで使い勝手を簡単によくできる。感謝。

投票所のシステム

選挙に行ってきた。そこで投票用紙を一枚差し出す機械を見て驚いた。あの機械が何をやっているのかは定かではないが、おそらく正確に1枚だけ投票用紙を有権者に渡すことが責務の一つなのだろう。そんな機械があるのはびっくり(皮肉を言いたいわけではない)。正確に一枚の投票用紙を渡すことは法律を遵守するために必要なことだし、機械を使わずに人の手で配る場合、複数枚渡すミスは確実に起きるだろう。なので、そこを徹底することは尤もだと思う。

では他に良い手はないだろうか。投票用紙をもらうためには、あらかじめ有権者に配布された案内票みたいなものを持っていく必要がある。それに投票用紙の機能をつけるのはどうだろうか、と思ったけど一意性の担保が難しそう。というのは、案内票は再発行できてほしいはず。たとえば郵送中に損なわれた場合や紛失した場合。再発行するには過去の案内票を間違いなくdropしないと一意性が担保されない。 dropのためにidを振るとかが考えられるが、無記名投票の原則に影響する。そういうわけで、案内票を投票用紙として使うのは難しそうだ。

ところで、最後の投票用紙を受け取るときに、案内票を回収された。これによって投票に行ったか否かがバレてしまうのが問題にならないかと思ったが、投票所を見張っていればバレることなのでいまさらか?

電子投票ではどんな性質をどのように担保しているのだろうか? やろうと思うとフィジカルな投票所は単に、バーチャルな投票所にアクセスできない人が集う場所になるだろう。委員会のひとはネットワークやパソコンなどのセットアップをする係になりそう。求められる能力が少し変わって、人を集めるのが大変そうだ。日本での実現には10年か20年くらいかかるだろうか。今の子供がそのあたりの教育を受けることを仮定したらの話だが。とはいえ、ipadとかでもできる気がするので、そんなに難しくない?安定性が一番の懸念か?インターネットアクセスも問題になる? とはいえそういう地域はいまでも問題になっていそうだし、電子的にしたほうが楽なイメージがある。

最後に、細かいことをたくさん書いたけど、僕はこのあたりの仕組みに興味があるのであって、現行の選挙システムのセキュリティに文句があるわけではないことを明記しておく。紙の投票用紙も好きだし。

Go Context

Golangで始めてcontextを使った。リポジトリは非公開なのであしからず。

Webスクレイピングツールを作っている。ランディングページを見て良さげなページへのリンクを集め、それらのリンクから欲しいコンテンツを収集する、というのがメインの動き。基本的には起動したら放置するのだが、ときどきいらないページをスキップしたくなる。このスキップ機能を追加するためにcontextを使った。

当初はcontext.WithCancelで良いかと思ったが、同じctxに複数回cancelを送信したいので良くない。結局context.WithValueで受信チャンネルをもたせた。あとはキー入力を受け付けるgoroutineを立てて、良い感じに対応する送信チャンネルからstruct{}{}を送れば良い。 (受信側はもちろんいる。もともとDOS攻撃を避けるために待つ箇所があったので、そこでtimer.Afterと並行して件のチャンネルを待つことにした。)

標準入出力をいろいろな部分から使っているので操作画面がとてもきたない。とはいえ自分で使う分にはストレスなく思ったとおりに動くので道具としては満足。

UbuntuでIpv6を無効化する

学内ネットにVPN接続してIEEEの論文を取得しようとしたが、学内からのアクセスだと認識されなかった。 IPv6を無効化するとうまくいった。

MacではGUIでIPv6をon/offできたが、UbuntuではCUIから設定した(参考ページ)。以下を実行する。

sudo sysctl -w net.ipv6.conf.all.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.default.disable_ipv6=1
sudo sysctl -w net.ipv6.conf.lo.disable_ipv6=1

設定は永続的ではない。永続的でないことは sysctl の manページや Red Hatのドキュメント を参照すると分かる。

逆に有効化するためには、...=1...=0 にすればよいはずだ。こちらは動作確認をしていないことに注意。

僕へ: ~/.local/bin/disable-ipv6.sh に上のスクリプトを書いた。パスが通っているので sudo をつけて実行せよ。

Ubuntu22.04でZoomの画面共有がうまくいかなかった件

追記: 先日画面共有しようとしたところ、うまく相手に表示されなかった。 Zoomから退出して、Ubuntuにログインし直し、ウィンドウシステムをWayalndからXに切り替えたところうまくいった。やはりXが安定なのか。

Ubuntu22.04で画面共有しようとすると、共有候補にホワイトボードしか表示されなかった。Zoomがknown issueとして公開していた。詳しくはリンク先を読めばよいが、waylandとの相性の問題とのこと。そのうち修正したいと書いてある。

対応策としては以下が紹介されていた。

  • Xorgを使う
  • ブラウザ版zoomを使う
  • OBSのようなソフトで仮想カメラにキャプチャ画面をつなげて、それをzoomで共有する

僕は面倒なので、常に全画面を共有するように設定した。これはzoomアプリの画面共有設定から行える。

NeoVimでクリップボードを使う

init.vimset clipboard+=unnamedplusと追記する。

Nvim では(vi や vim も)ヤンクやペーストで、nvim が管理するバッファを記憶領域として用いる。なのでクリップボードとは分離されていて不便なことが多い。

バッファとクリップボードをつなげれば良くて、その設定は上のコマンドで完結するようだ。ネットの記事には裏でpbcopyやそれに類するものの設定が必要、みたいなことをいう記事があるが手元の環境では上の1行を追加するだけで良かった。

:version
NVIM v0.6.1
Build type: Release
LuaJIT 2.1.0-beta3
Compiled by [email protected]

該当コミット:関係無い変更も同時にコミットしてる…。

複数の nvim プロセス間でクリップボードを共有できる(当然)のが地味に便利。 (普通は複数プロセス立てない?)

Begin Rust

Rustを勉強している。やはりライフタイムとか借用のあたりで苦労している。所有権やライフタイムはわかるのだが、借用の仕組みがイマイチわからない。特に可変な借用が同時に複数あってはならない、という規則で、同時にとは一体?という気持ちになる。同時の概念を理解させてくれる文書に出会えていない。これが原因で詰まった話をする。一応問題の回避はできたが、腑に落ちきっていない。

練習がてら、状態遷移系の各ノードで、与えられたCTL-formulaが成り立つかを調べるラベリングアルゴリズムを実装した。

labeling関数内でループを回す。考慮するすべてのCTL-formulaについて、条件が合えば add_label 関数を呼んでノードにその論理式への参照を追加する。ノード(State)につけるラベルはlabels: HashMap<State, HashSet<&Formula>>としている。

はじめ、add_labellabelsの可変参照をとるようにしていたのだが、借用チェッカに怒られてしまった。つまりadd_label : (..., labels: &mut HashMap<...>) -> ()add_labelsの呼び出しが複数回あって、それぞれに参照で貸し出したのがまずいようだ。直感では関数呼び出しが終わると参照を捨てるから安全だし、型システムもそう思ってくれる気がしていたのだが…。

add_labelに所有権を一旦渡して、変更した上で返却するようにしたら、怒られがなくなった。つまりadd_label : (..., labels: HashMap<...>) -> HashMap<...>とした。呼び出し元でlabelsに再代入することで所望の追加を実現できる。

これはこれできれいだと思うけど、はじめのアイデアも悪くない気がしてしまう。きっと修行が足りないのだろう。

ブログの設定を変えた

このブログはGitHub Pagesで公開していて、Hugoを用いて生成している。 Hugoの設定をろくに調べずに雑に書いていたのだが、今回少しドキュメントを読んで設定をした。

  • トップページ(記事へのリンクが並ぶページ)に記事の全文が表示されていたのが改善した。
    • asCJKLanguage = trueが効いていそう
  • テーマを変えた

最近Ubuntuを使い始めたり、講義でプログラミングする機会が増えたりした。得たTipsを積極的に記事にしよう。

NeoVimを使い始めた

Neo Vimが使っていて楽しい、という話。普段はVSCodeを使っていて、全然移行したわけでもないのだけれど、NVimは文字入力に対する反応が比較的早くて楽しい。ところで、いつも思うのだけど、日本語って打ちにくくないだろうか。IMEがいるのが良くない(これ以上の日本語入力方式はないだろうか?手書きよりは良いのだけれど)。

英語だとタイプミスしてもとりあえず近い単語を入力できて、大体スペルチェッカの修正機能で直せる。だからテキトーにキーを叩いてからあとはおまかせでok。入力ミスのダメージが小さい。それに対して日本語では、IMEが正しく認識できるように文字列を入力しないといけない。多少のまちがいはIMEは許容してくれることもあるけど英語の時ほどではなくて、大抵は一字一句正確に入力しないといけない。もしも間違って入力すると、IMEがうけっとた範囲の入力が全て誤って変換されるので(部分的にあっていても、正確に変換させるために全消しすることが多い)、ダメージが大きい。だから日本語入力は大変だ。

構文エディタ実装の考え

構文エディタとは、文字単位だけでなく抽象構文木単位での編集ができるエディタのこと。一般のコードでなく、プログラムを編集することは抽象構文木のノードやエッジの編集に対応する。ならば、そういう単位での編集をエディタが直接サポートすれば楽できるという発想(だと想像する)。

アイデアとしてはそれらしい気がする。でも流行ってるようには見えない。なんでか?

  • 需要が少ない: プログラムを新しく書くことは構文エディタの対象外であり、プログラムがあってそれに対する選択が主な操作である。なので構文じゃない編集機能はやはり必要で、そう考えるとそんなに欲しくない。とか?
  • 使い勝手が悪い: 使ったことがないし、具体的な機能を知らないので想像でしかないけど、おそらく思ったようにASTのノードを選択できないのではないか?そもそも選択したい「ASTのノード」が実は存在しないケースがある。(e1;e2;e3)のうち、e1;e2e2;e3のどちらかは存在しないはず。この辺りの問題は工夫すれば解決できそう。既存の実装はどのくらい工夫しているのだろうか。
  • 普通のエディタで十分: 前の/次のセミコロンまでジャンプとか、かっこまでジャンプとかがあれば済むケースは多い。
  • 実装が大変: 使い勝手を考えるとシンタックスハイライトとかよりは大変そう。抽象構文木をアレンジした概念が必要になりそう。

実装の話は Lisp なら簡単か。かっこを上手に扱う機能が入ればあとは普通のエディタの機能でほとんど構文エディタになる。

逆にシンタックスを弄りやすくして、普通のエディタが構文エディタ的な振る舞いをできるようにするのはどうだろう?Haskellはいい線いってる?文を消す <–> 行を消すの対応をほとんどの言語でしているので多かれ少なかれそういう側面がある。究極的にはどこまでいけるだろうか? エディタの基本操作は一文字入力/削除か。Vimでできることに広げるとどうだろうか。

関連して、エディタの話の制限を忘れて、言語デザインと使い勝手を考えたい。ソースコード –> AST –> typed-AST –> 実行結果 くらいの段階がプログラマが交流しうるプログラムの表現だろう(実行結果が入るのは理論的じゃないかもだけど、気持ちとしては入るはず。みんな実行のことを考えてプログラムを書くのだから)。

プログラムの概念は色々あるが、それらは必ず上のどこかの概念として捉えることができる。プログラムの挙動は実行時のことだし、行数はソースコードのこと。では型は? typed-ASTかそれよりも前のどこかだ。例えば型アノテーションついていればソースコードの時点でわかるが、付けずに型推論に任せていれば、typed-ASTにまで頭を巡らせないとわからない。

変数の参照はどうだろう。ソースコードなことが望ましい(lexical scope)けどそうじゃない困った言語もある。

いろんな概念があるが、なんにしても、早い段階でわかったほうが嬉しいことが多いだろう。でも一方でそれをわかあるためにプログラマがコンパイラの代わりをするのは非人道的だ。型アノテーションは大切だけど型推論を邪魔者扱いする人はいない(いないよね?)。これ以上具体的なことは言語の意味論によるだろう。HaskellとSchemeを同じ土俵で議論するのは大変そう。

読書感想: 入門 コンピュータ科学

本をざっくり読んだので感想をメモ。読んでよかった。

読んだ本: 入門 コンピュータ科学

おそらく情報工学科の学部で扱うであろう内容をまとめた本。プログラミングはしないけど、それ以外の座学は大体ざっくりまとまっていそう。僕は情報工学科の講義をとっていないので、真偽はわからないのだけど。あくまで内容はざっくりしていて、専門的な内容は省かれている感じ。インターネットとはとか、データベースとは、プログラミング言語とは、みたいなことが書かれていて、それぞれの難しさを指摘するところまでを解説してくれる。技術の実装方法や難しさの解決策とかは扱わない感じ(だったはず)。CS関連で知らないことが出てきた時に、どの辺を勉強すれば良いかの手がかりとなる概観を与えてくれたんだと思う。

この本を読んで、データベースとインターネットについてざっくりとした理解を得た。手を動かしつつ、必要に応じて勉強できそうだと思う。情報系の学部出身と言ったら、このくらいは完全に理解していて、さらに専門的なところまでいけることを社会から期待されている気がする。どのあたりの知見が僕に欠けているかを(多分)理解できたし、足りていなかったところをある程度補完できた。

上の感想文を書いて、僕のバックグラウンドに疑問を抱く人がいるかもしれないと思ったので説明しておく。

僕は情報科学科(数理・計算科学系)の出身でコンピュータサイエンスを一応専攻してきた。ただ、CSだけじゃなくて統計や数学も学科が扱う範囲に入っていて、例えば確率論の基礎(σ加法族とかから初めて中心極限定理まで)とか集合と位相(和集合演算というのは…とかから濃度、ベルンシュタインの定理、選択公理をみて、位相論では分離公理(だったっけ?)とか連続性を扱った。付録的な立ち位置だったけど、有理数全体の集合を距離空間として見たとき、それを完備化して実数を構成する様も追った。)。これらは学部2年の話で、他にも色々講義があった。その傍でCSの講義も開講されていた感じなので、ある程度カルキュラムから端折ったCSの内容があったのだろうと思う。統計や数学の人からしても、端折った箇所があったと感じてるのではないかと想像する。数学か行くべきと発言していた人もいる。そういうわけで、今回本を読んで知識を補完できてよかったと思った。

学科の選択に後悔はなくて、色々楽しそうな領域を見て、そうでもないかなと判断できた。その中で生き残った楽しい領域で今勉強しているのだから、とりあえず満足。なんならプログラムングなんてしたくないと、学部3年の終わりまで思っていたし。

学部の授業で講義資料を作るのやめて、こういう教科書を指定するのが学生のためだと思う。講義資料を作るのは先生の自己満足なのだろうか。先生にとっては勉強になるだろうが、学生からすると比較的完成度の低い資料を与えられて、成績のためにそれに従った勉強を強いられる。授業内容は先生の専門になるほど細かいことではなく、多くの人が知っている教養的な内容なのだから、信頼できる文献を学生に与えることが教える側の責任の取り方なんじゃないかと思う。教科書を用いて講義を進めることにどんな問題があるのだろうか。

本を読むときに、人から与えられて読むか、自分で発見して読むかによって学習のモチベーションは大きく変わる。人から与えられた時には粗が気になるが、自分で読む時に美点に目がつく傾向がある。もちろん白黒はっきり分かれるわけではなくて、どちらかというと、くらいのふんわりとした傾向でしかないけど。その辺りを気にして良い教科書を紹介しないで、やる気のある人が自分で発見する機会をとっておくために自作の講義資料を使う、とか?

そうだとしたら、その判断は微妙に感じる。講義で一回目を通して、その時は読む気が起きなかった教科書も、後々(数ヶ月、1、2年後)に読み返すことは少なくない。その時には自分で発見して読むときと同じようなモチベーションで僕は読める。そういうわけで、良い本を知らずに過ごすリスクを減らすためにやはり先生には紹介してほしいと思う。それも、ただタイトルと概要を述べるだけではなく、実際に授業で本の中身を扱うことで内容を学生にわからせてほしい。

教訓:

  • 良い論文とか文献は積極的に紹介する。
  • 紹介する時には、内容まである程度紹介するとよい。

今回は読書感想文なので読んだ本の内容は紹介しない。ここでCS概論述べてもしょうがないし。僕みたいな人間がこの本を読んでどういうふうに良いと思ったかをメモすることで、似た境遇の人とかの助けになれば良いな、という程度のモチベーションでした。

My First Post

このサイトはHugoとGitHub Pagesを用いて運用しています。これらとGitHub Actionを組み合わせることで、マークダウンで記事を書いてGitHubにpushするだけで更新できるブログを構築しています。 Hugoは静的サイトジェネレータの一つで、設定を済ませたあとは、マークダウンで記事を書くことでコマンド一発でWebページのソースを生成してくれます。 GitHub PagesはGitHubに保存したコンテンツをWebページとして配信してくれるサービスです。

この構成は僕のアイデアではなくて、いろいろな人が手順を紹介してくれています。僕が参考にした記事を以下に紹介します。

  • Hugo on GitHub
    GitHub Actionを用いたHugoの運用方法を解説しているドキュメントです。記載の方法に従うことで、mainブランチにmarkdownを書き、gh-pagesブランチに配布するためのHTMLなどを自動で生成できるようになります。
  • Quick Start Hugo
    Hugoの始め方。Hugoのインストールからlocalhost経由でのサイト表示の確認までを教えてくれます。