- Git の
help.autocorrect=1 は、タイプミスしたコマンドを修正したあと 0.1秒 だけ待って実行するため、ユーザーが確認したり取り消したりするには、実質的に即時実行に近い
- この挙動は Git のデフォルトではなく、既定の設定では不明なコマンドは実行されず、似たコマンドだけを表示して終了する
- 2008年の議論で
help.autocorrect はブール値ではなく decisecond 単位の待機時間 に変わり、1 は true ではなく 0.1秒待機として解釈されるようになった
- 現在の
help.autocorrect は 0、正の数、immediate、prompt、never をサポートしており、確認してから実行したいなら prompt のほうが安全な選択肢である
- Git のコマンド推定は修正された Levenshtein distance ベースで、Scott Chacon は
1 とブール文字列をより自然に扱うパッチを提案している
git pushy が git push として実行されるまで
@dhh が git pushy と入力したとき、Git はこれを git push と推定し、実行前に 0.1秒 しか待たない
- 0.1秒は、ユーザーがコマンドを読み、正しいか判断し、取り消すには短すぎる時間である
- この挙動は Git のデフォルト動作ではなく、
help.autocorrect 設定が有効なときに現れる
デフォルト動作と help.autocorrect の誕生
- Git のデフォルトの反応は、不明なコマンドを実行せず、文字列の類似度に応じて候補となるコマンドを表示して終了することだ
- 例:
git pushy では push が最も近いコマンドとして表示されることがある
- もともと Git は、不明なコマンドに対して単に「Git コマンドではない」とだけ返していた
- 2008年、Johannes Schindelin は既知のコマンドを走査して最も近いコマンドを探し、近い候補が1つだけなら実行する小さなパッチを提案した
- Alex Riesen はこれを
help.autocorrect 設定で制御するパッチを出し、当初この設定は ブール値 だった
- Git の設定でブール値は
1 を true として解釈できる
- そのため、当時の文脈では
help.autocorrect=1 は自動修正を有効にする値のように見えた
1 が 0.1秒になった理由
- Junio Hamano は、
help.autocorrect を単なるブール値ではなく、実行前に待つ decisecond 数 にしようと提案した
- decisecond は 0.1秒単位である
- ユーザーに
^C を押す機会を与える意図があった
- その後、受け入れられたパッチで設定値は待機時間に変更された
- その結果、
help.autocorrect=1 は「機能を有効にする」ではなく、「1 decisecond、つまり 0.1秒待ってから実行する」という意味になった
- なぜ decisecond が適切な時間単位として選ばれたのかは、議論には残っていない
現在選べる設定値
- Git のドキュメントでは、
help.autocorrect は次の値をサポートしている
0: デフォルト値で、候補コマンドだけを表示する
- 正の数: 指定した decisecond 後に候補コマンドを実行する
immediate: 候補コマンドを即座に実行する
prompt: 候補を表示して実行するか確認する
never: 候補コマンドを実行も表示もしない
- 確認してから実行したいなら、
prompt は大半のユーザーにとってより合理的である
- 設定例は次のとおり
git config --global help.autocorrect prompt
- 以後
git pushy のようなコマンドを入力すると、push を実行するか [y/N] で確認する
Git が似たコマンドを選ぶ方法
- Git はやみくもにコマンドを推定するのではなく、ある時点を超えると距離が遠すぎると判断して候補を出さない
- 動作例は次のとおり
git bass は rebase に十分近いため、実行確認が表示される
git bassa はもはや rebase と見なせないと判断され、候補は実行されない
git dm は am と rm の両方を似たコマンドとして表示する
git dma は am 1つにマッチする
- このロジックは修正された Levenshtein distance アルゴリズムに基づいている
- ある文字列を別の文字列に変えるのに必要な文字編集コストを計算する
- 一部の操作はほかの操作より高いコストとして扱われる
- Git にはハードコードされた cutoff があり、既知のコマンドとのコストが大きすぎると、ユーザーの入力ミスが大きいと見なして推定しない
1 の解釈を変えようとするパッチ
help.autocorrect=1 は人間の感覚では事実上 immediate と同じなので、Scott Chacon は 1 を 0.1秒待機ではなく 即時実行 として解釈する小さなパッチを書いた
- Junio Hamano は
"1" だけを特別扱いするのではなく、yes、no、true、off のような ブール文字列値 も適切に解釈しようと求めた
- パッチの第2版は、こうしたブール文字列の処理まで含める方向で進んでいる
- パッチが取り込まれれば、今後の Git バージョンでは
help.autocorrect をめぐる混乱が減る可能性がある
1件のコメント
Hacker News のコメント
70年代に Hal Finney が Mattel Intellivision 向けの BASIC インタプリタを 2KB ROM に収めようとしていて、1バイトたりとも貴重だった。
構文エラーを表示するために、すべてのエラーメッセージを
EH?に縮めていて、本人はそれをかなり誇らしく思っていたというのが今でも笑える。EH?は昔ながらの インタラクティブフィクション ゲームのテキストパーサー応答としてもよく似合いそう。少し関連して、昔の BASIC の亜種には
PRINT文を?と書けるものもあったが、メモリ節約というよりは REPL での入力時間を減らす目的に近かった気がする。edはすべてのエラーに?1文字しか使わないから 3倍節約できる。Eh?を使った最も古い例は、RAND の JOSS で見た。[https://en.wikipedia.org/wiki/JOSS#/media/File:JOSS_Session....](https://en.wikipedia.org/wiki/JOSS#/media/File:JOSS_Session.jpg)
https://en.wikipedia.org/wiki/JOSS
メモリは約 5KB だったが、Intellivision と比べると機械の重量は約 5,000 ポンドだった。
だから、ある単語を推測して5文字すべてが緑になっても、2つの正解のどちらでもないことがある。そういうとき右側に単に
Huh?と表示するようにしたら、そこをみんな気に入っている。根本原因は 設定名の付け方が悪かったこと だと思う。
もともとの設定が
help.autocorrect_enabledのようにブール値っぽい名前だったなら、整数値を受け取って 0.1 秒単位として解釈しようという要求は筋が通らなかったはずだ。代わりにhelp.autocorrect_accept_after_dsecのような別設定が必要になっただろうし、dsecはあまりに馴染みがないので、使う人なら調べざるを得なかったはずだ。コードレビューではいつもこういうことを求めている。曖昧になり得るなら、変数名に 単位 を入れるべきだ。たとえば
int timeoutはint timeout_msecにすべき。設定名ではこれが100倍重要になる。設定は公開インターフェースの一部なので、後から変えられないからだ。
C++ の文脈では、大まかな基準として
void FooBar(std::chrono::milliseconds timeout);は問題ない。関数シグネチャなので、見るときに型も一緒に見えるからだ。だが変数で
timeoutはよくない。99% の場合、実際の利用箇所ではauto timeout = gl_timeout;やFooBar(timeout);のように見える。C++ では
autoをよく使うので、重要なときに正確な型を追跡するのがかなり面倒になる。C++ で作業するときは IDE や言語サーバー付きのエディタを使っているが、5秒ごとにマウスオーバーして型を確認する時間はない。
「1ms 後に実行します」というメッセージを見たら、すぐに設定を確認していただろう。
TimoutSec=60もTimeoutSec=1minも使える状態になる。むしろ単位を名前に入れないほうがよかったと思う。ただし、その特殊なケースを除けば完全に同意する。
これまで見つけた最善の代案は、値に単位を受け付けることだ。たとえば
5 secondsや5sを許可し、単なる1は不正な値として扱う方式だ。intの使用自体を指摘し、代わりに単位を正しく伝える 値クラス を勧めている。たとえば
Second(2)やMillisecond(2000)のような形だ。もちろんこれは言語の機能と慣習による。
xmobar も似ているが、もっと問題のある場所で 0.1秒単位 を使っている。各セクションをどれくらい頻繁に更新するかを宣言する設定だ。
サンプル設定の数字が小さすぎてミリ秒のはずはなさそうなので、合理的に秒だと仮定してしまい、その結果、意図より10倍頻繁にコマンドを実行することになる。
この問題に起因する偶発的な負荷急増を何度も見た。
かなり悪い設計に見える。
ただし1番は記事の読み違いの結果で、「以前の値」は git には存在したことがなかった。
1=trueを1=0.100ms の確認遅延に変えるようなことは、ほぼ常に避けるべきだ。既存の値があったなら消したくなるだろうが、設定名も変えるのが正しい。help.autocorrectの設定引数が時間であり、ほとんどのユーザーにとって標準的ではない単位で測られるというのは単純に悪い。有効化はブール値で受け取り、確認時間は小数値で調整できるようにすべきだ。その変更は2008年に、そのオプションをブール値として追加しようとした初期パッチのコードレビュー中に起こったもので、メインの git ツリーにコミットされる前だった。
これは設計というより、有機的に育った脇枝 に近い。
これは 機能過多(creeping featurism) の良い例だと思う。
よく見ても不要な複雑さを持ち込み、悪く見れば、それに依存する人たちが自分のしていることにあまり注意を払わなくなる。
上矢印を押してミスを直すのはそんなに難しいのか? 「だいたい合っている」ものを自動で実行するのは、意図しないことをしてしまうためのレシピだ。
ミスはここにあった。既存の設定を別の意味に再利用するのではなく、新しい設定を追加すべきだった
help.autocorrectはオン/オフ、help.autocorrect.millisecondsはどれだけ待つか、というふうに分けるべきだった他のシステムにも似たようなミスがある。たとえば MySQL の
innodb_flush_log_at_trx_commitは 0 なら無効、1 なら有効で、2 は後から特殊な意味として追加された1が何を意味するのか推測しようとする型のない設定言語にある今では
1はブール値の真のままにしつつ、他の整数は 0.1 秒単位にしており、さらに深みにはまっているYAML の悪名高い
no == falseと同じような怪しいロジックだ記録上最速のランダム反応時間は 1.5ds 程度で、偶然にも平均的なゲーマーの反応時間もそのあたりだが、非ランダムな反応はずっと速くなり得る。たとえば拍子に合わせて反応する場合がそうだ
そこまで速くしたいなら可能で、呼び出し速度も比較的安定しているはずだ。おおよそ数ミリ秒のレベルだろう
1年前に自動修正機能を有効にして 3 秒に設定したが、2つのことに気づいた
この記事を読んで、1年ぶりにオフにするきっかけになった
もちろんシェル履歴は「汚染」するけれど
履歴をさかのぼって、どうせ動くからという理由で最後の間違ったコマンドをそのまま再利用しても同じことだ
0.1 秒単位は本当に変わった選択だ
遅延時間はミリ秒か秒で指定するほうがよく、どちらもコンピューティングでははるかによく使われる
decisecondsが git 専用の単位で、「決定を下すのに必要な秒」のような decision-seconds を意味するのかと思った xDちなみに英語は母語ではないが、メートル法を使う文明化された地域の出身だ
0 秒と 1 秒の差は何かを待つときには明らかに長いが、n ミリ秒と n+1 ミリ秒の差は実用的に使うには細かすぎる
反応時間は刺激の種類によって異なり、聴覚は視覚より少し速く、触覚はそれよりさらに少し速くて 90〜180ms 程度だ
だから git がエラーメッセージの代わりに一発殴ってくれるなら、かろうじて反応する時間があるかもしれない
10ms でそれは十分だろうか?
この記事を読んで、ソフトウェア考古学と「プログラマー考古学者」という言葉を思い出した
後者の概念については Vernor Vinge に感謝すべきだ
ところがその概念はプロローグで少しほのめかされるだけで、その後はまったく出てこない。考古学の効果は筋書きには関係しているが、「ソフトウェア考古学者」は物語の中で意味のある人物ではなかった
釣られたような気分だった