2023-11-04

F# でコマンドレットを書いてる pt.22

pocof の開発をした。

キー入力に async expression を利用する、がメイン。 krymtkts/blog-fable の開発で F# の async expression を触ったので、ちゃんと F# らしく pocof を改善してこうという気になった。

元々はキー入力されるまで Console.KeyAvailable でチェックして無限に再帰する形で実装してたのだけど、このコメントによれば、どうも継続を使ったら非同期にできるらしい。 asynchronous - Async to wait for a key in F#? - Stack Overflow

回答者が Invoke-Build の nightroman サンというのも渋い。ぜひ使いたい。

Async.FromContinuations でキー入力待ちを非同期にして Async.RunSynchronously で結果を待ち受ける。 これによって無限ループで無駄に CPU 回していたやつが回避される、と。 ひとまずこうした。

            let getKey () =
Async.FromContinuations(fun (cont, _, _) -> Console.ReadKey true |> cont)
|> Async.RunSynchronously

これでキー入力してないときの CPU 使用率はかなり改善された。

元々コピペした文字列を Console.ReadKey で 1 文字ずつ読み取って表示の処理まで回してるからパフォ良くない課題がある。 今回の非同期化で幾分シンプルになったし、 Console.KeyAvailable と再帰を組み合わせて文字列丸ごと読み取ることで改善できそうな雰囲気も感じてきた。

因みに Async expressions - F# | Microsoft Learn にはこういう定石パターンみたいなの載ってなかったので、今回のように実例から学ぶのがいいのかな。

今回のキー入力を非同期にしたやつは、先にググったらそのまま良さそうな snippet が見つかったから GitHub Copilot には相談しなかった。 でも次は聞いてみたらなんか良いコード出してもらえるかもな。

他に Lazy Expressions - F# | Microsoft Learn なんかも試したいけど pocof に活用できることないかもな...という直感。まずコードを漁って実例から学ぶとしよう。