shellのpipeを使いつつ入力とかを受け付ける方法

ちょっと最近やり始めた(正確に言うとやり直し始めた)ツールづくりで、どうやったかを毎回調べてしまうので、備忘録的に書いておきます。 <!–more–> pipeしたときの標準出力とかの状態 Unix(Linux)で、shellからプログラムをpipeで繋いで起動した場合に、標準入出力がどうなるか?というのは、以下の記事にわかりやすく載っています。 Qiita Linuxのパイプをちょっとだけ理解する さて、pipeは標準入出力、特にinteractiveな操作を必要とする場合、標準入力が別コマンドの標準出力が繋がっていることが問題になります。上の記事にもある通り、キーボードを受け取る口が無くなるので、pipeの右側にあるコマンドは、そのままではdaemonみたいな感じになっています。 また、この状態でさらにpipeで繋がれると、当然ですが標準出力が別のコマンドの標準出力につながることになります。進捗などを表示したりするときに標準出力に出したりすると、別のコマンドに余計な出力をすることにもなり、あんまりうれしくありません。こういうのをどうやって解決したら良いか? /dev/tty を使う そんなときには、character deviceである /dev/tty を使います。 http://tldp.org/HOWTO/Text-Terminal-HOWTO-7.html#ss7.3 この辺りに説明が書いてありますが、 /dev/tty は、 現在のprocessに対するterminalの制御 を行うためのデバイスです。つまり、このデバイスファイルに対して出力するとターミナルに対して入力したものとして扱われ、このデバイスファイルを読み込むと、書き込んだものを読める、ということになります。 試してないのであれですが、異なるプロセスが同時にこのファイルを開いても、最終的には /dev/pty/N というキャラクターデバイスに割り当てられるはずです。なので、競合とかは考えなくていい、と思います。 簡単に書くとこんな感じになります。入力のみ受け付ける、とかであれば、 RDWR を RDONLY にすると良いです。 (* OCamlで書くとこんな感じ *) let () = let fd = Unix.openfile "/dev/tty" [Unix.RDWR] 0o666 in let stdin' = fd and stdout' = fd in (* stdin' stdout'を使う *) Unix.close fd かなりシンプルですが、これでちゃんと動きます。Windowsでは確か仕組みが異なるため、それも対応するとなると、また別の方法が必要ですが。実際に別のptyが割り当てられていることを確認する場合、以下のような手段があります。 tty コマンドというそのまんまの名前のコマンドを使うと、今のプロセスで標準入力に接続されているファイルがわかります ps コマンドのtty欄を見ると、そのコマンドの標準入力に繋がっているファイルがわかります 特にpsコマンドの方だと、shell scriptの中から起動されたコマンドのttyは、起動元のshellのttyと同一であったりと、仕組みが思い浮かぶような感じにもなっていて面白いので、覚えておくといいと思います。 最後に 今回は久しぶりにhow to 系の記事でした。またこういうのも書いていこうと思います。How To系ってちゃんと書こうとするとハードルが高いのと、調べればわかるので書く気力が湧けば、ですが・・・。 ...

July 12, 2020 · derui

最近はpure CSS + PostCSSを使っている話

緊急事態宣言は解除されましたが、相変わらず在宅勤務です。また満員電車とかに慣れるための修練が必要になりますね・・・ 今日は ネタがないので 、最近の趣味におけるCSSの書き方を簡単に書いていきます。 <!–more–> なぜPure CSSに回帰したのか 以前は、自分で書くのは全部SCSSで、仕事ではPure CSSでした。SCSSの現場もありましたが、余計な依存を増やさないでくれ、という依頼があった時はPure CSSを書くようにしていました。 だいぶ前(PostCSSが出るより前)は、ある程度構造化したり共通化したCSSをかきたいなー、ってなった場合、SCSSなりSassやLESSといったAltCSSを選択するしかありませんでした。 私の把握している範囲では、です。他にあったのかもしれませんが、当時リーズナブルな手法と言ったらAltCSSだったと思います。 ただ、AltCSSはAltJSと同じような課題を抱えていました。今も余り変わらないと思いますが・・・。 Toolに追随する必要がある 機能の増えた・減ったなどに対応する必要がある 結局生成されるのはCSSなので、CSSの知識+アルファが必要 色々やりすぎて結局保守性が下がる PostCSSとCSS Custom Variables PostCSSは以前から知っていましたが、実際に使ったことはありませんでした。使うだけなら、npm/yarnでCLIを入れれば使えます。 $ yarn add postcss-cli PostCSSとAltCSSの違いは、色んな所で言われていますが、大きく他と違うのは、 あくまでCSSのPostprocesser である、ということだと理解しています。 そのため、PostCSSのpluginは大抵未来のCSS標準を試すための試金石だったり、autoprefixerに代表されるutility系が多いです。言語の根幹を変更するようなものは、ユーザーが自ら入れなければならないため、危険が少ないのも利点だと思います。 また、AltCSSを利用する最大(個人的に)の理由だった、変数についてもCustom Variableという形で利用できるようになっています。 SCSSの変数とは異なるものではありますが、逆にSCSSの変数では出来ないことも出来るので、メリット・デメリット両方があると思います。 特に、テーマ機能のようなものを使う場合、Custom Variablesの方が使いやすいと思います。色々問題もありますが。 なにより、最悪PostCSSが使えなくなってもダメージがそれ程でもない、というのが安心感あります。 最近使っているPostCSSのPlugin postcss-extend-rule @extend を使えます、が、現状特に使っていないという・・・ postcss-import cssのbmportと違い、inlineでの展開が出来ます。 postcss-nesting SCSSとかのようなnesting ruleを書けます これだけしか使っていませんし、これ以上入れる気もあんまりしていません。 正直nestingもいらないと言えばいらないんですが、疑似要素とかを書く時に重宝するので入れています。 これくらいしか入れていなくても、それほど問題なくサクサクと書けています。複数人開発じゃないから、というのもありそうですが・・・。 とりとめのない終わり 実際、SCSSを書いてもCSSを書いても、セレクタの命名規則だったり詳細度の話だったりは変わりません。ので、ちょっとした書き味の違いを求めるのであれば、標準であるCSSをそのまま書いたほうがメリットがあると、最近は思っています。 Sassに疲れた、とか機能を使い切れなくて悔しい、とか感じているようなら、一度Pure CSSに戻ってみてはどうでしょうか。逆に新鮮だったりするかもしれませんよ。

June 10, 2020 · derui

cmigemoの代わりに自作のmigemoを使うようにしてみた

GWでも変わらずこもってますが、よく考えたら常日頃こもってるので、いつもと変わらんじゃないか、とも思ってきました。 さて、最近Emacsの設定や開発環境を見直したりしていますが、その中で久しく動いていなかったmigemoを動くようにしてみましたので、それについて書きます。 <!–more–> なんで動いていなかったのか そもそもなんで動いていなかったのか、ですが、cmigemo自体は自分でbuildしていたものがあったので、動かすことは出来ました。ただ、 cmigemoが依存しているnkfが、gentooでinstallできない(ので、cmigemoもinstallできない) nkfがpython2.7のみに依存しているのですが、私の環境は脱python2.7しているので、installできなかった。。。 という一点に於いて、cmigemoをこのまま使うべきかどうか、中々判断しづらかった、というのがあります。 cmigemoの代替は・・・ cmigemoでなければどうするか、ということですが、実は選択肢はほとんど無く、originalのmigemoくらいしかありません。 http://0xcc.net/migemo/ ただ、cmigemoはC言語で実装されているので、速度がダンチです。また、私の環境ではRubyが入っていないも同然なので、migemoのためだけに入れるのも・・・と感じていました。そんな時、ふと以前OCamlで実装し直したmigemocamlというのがあるのを思い出しました。 これにEmacs用の実装を入れればいいんじゃないか? という天啓(?)を受けて、実装することにしました。 こぼれ話:なんでOCamlでmigemoを実装していたのか 趣味で作っているOCamlアプリケーションで、migemoを使った検索をしたかったんです。ただ、Windows/Linux両対応する、というときに、C Libraryを使うのは色々とめんどくさいですし、stubの実装が馬鹿になりません。 pure OCamlで作ってしまえば、Windows/Linux両対応部分が大幅に減るので、色々楽じゃん、ということで再実装しました。 cmigemoとほぼ遜色のない速度を出せているので、個人的には満足です。 Emacsの正規表現をだせるようにする migemocamlで出力している正規表現は、PCREを前提とした、最小限の特殊文字だけ利用しています。ただ、Emacsの正規表現は、歴史的な事情から、特殊なエスケープが必要になっています。 https://flex.phys.tohoku.ac.jp/texi/eljman/eljman_218.html 詳しくは上掲のサイトに載っていますが、今回対応した分だと、次のような違いがあります。 (と) は、 \(と\) にしないといけない | は、 \| にしないといけない ただ、OCaml上での実装は、こういった違いをmoduleで表現して差し替えられるようにしたくらいで、あんまりいじってはいません。 https://github.com/derui/migemocaml/pull/1 ocamlformatをかけたのでめっちゃ差分が出てるけど・・・ Emacsに設定する 無事に実装できたので、Emacsで設定してみます。なお、Emacsの設定では全面的に leaf.el + straight.el になっております。 (leaf migemo :straight t :commands migemo-init :custom (migemo-command . "~/.opam/4.09.1/bin/migemocaml") (migemo-options . '("-q" "--emacs")) (migemo-dictionary . "/usr/local/share/migemo/utf-8") (migemo-user-dictionary . nil) (migemo-regex-dictionary . nil) (migemo-coding-system 'utf-8-unix) (migemo-use-pattern-alist . t) (migemo-use-frequent-pattern-alist . t) (migemo-pattern-alist-length . 1024) :config (migemo-init)) migemo-dictionary の設定がcmigemoと異なりますが、それ以外はcmigemoと同じ設定でいけるようになっています。あとは、 avy-migemo とか helm とか、migemoをsupportしているpackageを使えば動作します。 ...

May 5, 2020 · derui

qmk_firmwareの日本語配列からかな入力をする

すっかり在宅に慣れてきましたが、ちょっと出かけることも出来ないというのが割とストレスですね・・・。 最近は出勤時間分の時間が空いたので、環境の改善をよく行っています。そんな中で、日本語入力も改善したので、それについて書こうかと思います。 <!–more–> qmk_firmwareと日本語入力 いくつかqmk_firmwareでカナ入力の方式を実装してきましたが、今までの実装だと、以下のような問題がありました。 どうしてもローマ字で入力させる必要があったため、マッピングが肥大する 肥大すると、当然ながら他の機能を追加できないため、使い勝手が悪い 濁音を後置する場合、どうしても不自然になる 本来であればIME側でやってくれることを、ファームウェア側でやる必要がある 遅延で入力させることもできるが、いかんせん表示がかなり不自然になる 特にnew stickney配列を使うようになって顕著なのが、濁音後置になったため、濁音の入力時に考えなければならないことが増えました。 timerを使った遅延入力にすると、出力自体が遅延するため、今入力している内容を把握するのが大変です。濁音キーを入れた時に、一回入れた文字を消して新しい文字を入れる、ということもしてみたりしましたが、これはこれで誤動作が多く、今ではお蔵入りになっています。 なぜIMEのかな入力を使わないのかと IMEでローマ字ではないかな入力を使えば、こういう問題はある程度解決されます。今までやってなかったのは、ひとえに マッピングがめんどくさい という点が大きかったです。 また、ローマ字では、キーボードのレイアウトがUSでもJPでも全く問題なく扱うことが出来ますが、かな入力では、USではそもそも入力できないキーコードが必要になります。こういった点を解決できるのかが把握できなかったため、放置していましたが、一念発起して対応してみました。 対応した結果 こんな感じになりました。key sequenceが1文字になったことと、濁音と半濁音のハンドリングを自分で行う必要が無くなったため、全体の容量は減っています。 https://github.com/derui/qmk_firmware/tree/master/keyboards/crkbd/keymaps/derui ただ、小書き文字に対する対応が必要であるため、母音を入力するときだけは遅延が発生する状態です。慣れればdelayを低くしてもいいとは思いますが、今のところは置いておいてます。この小書き文字の部分をスマートに実装できれば、かなりの使い勝手になりそうだと勝手に思ってます。 また、Emacsでの設定も追加しました。 (setq mozc-keymap-kana mozc-keymap-kana-101us) この対応をした結果、以下のようになりました。 Fcitx + mozcでは特に問題なく使える Emacs + mozcでは、 ろ と ー に対応するkey codeをハンドリング出来ていないため、本来の入力と異なるmappingが行われてしまっている この2つ以外は、問題なくnew stickney配列を再現できている Emacsでもfcitxを使って、mozc.elを使わない、とかすればいいかもしれないが・・・ mozc-keymap-kana-106jp というkeymapに変更すると、異なる文字が入れられるようになってしまう 多分USレイアウトとJPレイアウトで記号が異なる部分で違っている USレイアウトにないkey codeをハンドリングすることが出来ればなんとかなるので、mozc.elの中を覗いているところ 仕事で使うMacでは試していない ローマ字でいいじゃん、という誘惑との戦い 正直、これだけやっていても、仕事で急いでいるときにはローマ字で入力してしまっているという体たらくなので、ローマ字でいいんじゃないか?と思うときがないわけではありません。 特に、価値のあること(大抵金銭的なもの)以外に時間を使うことに対して否定的な環境にいると、非効率自体が無駄とみなされがちです。 しかし、異なる入力方法に親しむということは、異なる能力開発をしている、ということでもあります。この入力を自然に行うためにはどうするべきか?という問いに答えるのは自分しかいません。問題解決能力を鍛えるということは、みんな大好き人生100年時代にもマッチするのではないでしょうか。 なんだかんだ書いてみましたが、つまるところ自分の趣味なので、まー好きにさせてくれよ、というところでしょうか。 かな入力はIMEに任せよう 濁音後置型のかな入力は、IMEのJISかなに任せると楽が出来ます。新下駄配列とかの濁音も一モーションで入力するような配列では工夫する必要がありそうですが、送信するキーシーケンスの数は減るはずです。 qmk_firmwareでカナ入力を実装している方の参考になれば幸いです。

April 29, 2020 · derui

Enzymeの代わりにtesting-libraryを使うようにしてみた

いろいろコロナの影響が出てきましたが、いかがお過ごしでしょうか。会社でも東京勤務は基本的に在宅となりました。 今まで、ReactとかのIntegration testにはenzymeを使っていましたが、Preactに切り替えた際に課題が多発したため、なんとかして解決してみました。 <!–more–> Preact + Enzymeの課題 Enzymeは元々jsdomとセットで利用していたのですが、 React -> Preact に切り替えたとき、仕様の違いによってテストが動かなかったです。 主な違いとしては、 Preactではshallow renderingが出来ない Portalで色々問題がある これはReactでも色々あるみたいなので、Preactに限ったことではないですが React/Preactでeventの扱いが全く異なる といったあたりです。特に既存のtestでshallow renderingを多用しており、大抵は動くのですが、 React.createPortal を使っているところが全滅でした。 testing-library React.createPortal および Preactの createPortal ですが、いずれも libraryの管理範囲外のDOMにrenderingする というcomponentを構成します。 React.jsの場合、EnzymeのAdapter側でかなり色々やって対応しているようですが、Preactの場合、 Componentを完全にrenderingする というそもそもの仕様から、対応が無理っぽいです。ちゃんと見ていませんが、third partyがlibraryの内部実装に依存している、というのはあまりよろしくないように感じます。 また、よく考えると、jsdomとはいえ実DOMにrenderingするということは、もう それはIntegration Testじゃないのか? という意見を見て、 ( ゚д゚)ハッ! となりました。そんな折に見つけたのが、testing-libraryです。 公式によると、testing-libraryは次のようなソリューションを提供するlibraryです。 The Solution The Testing Library family of libraries is a very light-weight solution for testing without all the implementation details. The main utilities it provides involve querying for nodes similarly to how users would find them. In this way, testing-library helps ensure your tests give you confidence in your UI code. ...

April 5, 2020 · derui

ProtocolBufferを使ってWebSocketでRPCをする

超暖冬だったりコロナウイルス騒ぎだったりと、なんと言うか全く落ち着かないこの頃、いかがお過ごしでしょうか。私は投資信託の金額が乱高下してなんとも言えない気持ちになっているのと、リーマンの再来とか言われてビクッとしている今日この頃です。 そういう世間の流れを一旦見ないことにして、最近やっと動作の確認が取れた、ProtocolBuffer + WebSocketによるRPCの方法を書こうかと思います。 <!–more–> さきにまとめ ここを見たら後の文は見なくていいんじゃないか疑惑が。 gRPCを利用しない場合、ブラウザと双方向でやり取りできるのはWebSocketくらい WebSocketではHTTP/2のようなpathは使えない JSON-RPCのような形式を使うとよい command的なもののEnumはデフォルト値をエラーとして使うといい 以降ではこれについての詳細を。 gRPCの利点と課題 ProtocolBufferを利用したRPCというと、gRPCがまっさきに出てきますし、ProtocolBufferを使う方の半分くらい(要出典)はこれが目的でしょう。ただし、それはgRPCを利用できる環境があるから、という前提が当然あります。 ではgRPCを利用できる条件とはどのようなものでしょうか。その大前提として、 HTTP/2 の存在があります。元々HTTP/2のベースであるSPDY自体、Googleが開発していたためでしょう。HTTP/2とHTTP/1.1の違いは次のようなものがあります。 HTTP/2はバイナリベース multiplexy Serverからクライアントに対してpushすることが出来る headerの圧縮が必須 gRPCは、これらのHTTP/2が持つ利点を活かして、高パフォーマンスかつ低遅延なRPCを実現しています。 間違いなく次世代のネットワークはHTTP/2ベースになると思いますが、OCamlではHTTP/2を利用する難易度がかなり高いです。また、ブラウザからJavaScriptを利用してHTTP/2接続を利用することも今は出来ません。 いま個人的に作っているツールでは、serverからのpushを必要としているので、HTTP/2が使えない場合、WebSocketを使うしかありません。しかし、WebSocketではgRPCを使うことは出来ません。 (gRPCは、HTTPのmethodやpathを利用して色々行っているため) WebSocketでRPCを実現しよう 改めて、WebSocketは以下のような特徴を持つprotocolです。 HTTPからのハンドシェイクをもって切り替える 一本のconnectionだけで通信を行う 双方向の通信が可能 完全に非同期 あるメッセージを待つ、というようなことは出来ない 参考:WebSocket Protocol仕様の日本語訳 しかし、JavaScriptでWebSocketを扱うプログラムを書いたことのある方はわかると思いますが、WebSocketは message という塊のやり取りしか出来ません。HTTP/1.1のようなpath/methodというようなものを使うことは出来ません。 RPCを実装する上では、そのメッセージがどのcommandに対するresponseなのか?を判別する必要があります。 ではどうするか?となりますが、ここで参考になるのが JSON-RPC です。 JSON-RPC自体、非常にLightな仕様ですが、大事なのが requestとresponseが完全に非同期である ということを前提としていることです。この特徴から、WebSocket上でも特に問題なく動作します。ということは、 JSON-RPCっぽいのをProtocolBufferで実装すればいいんではないか? という考えが浮かびます。 ProtocolBufferにJSON-RPCっぽいのを実装する では早速protoファイルを作ってみます。 syntax = "proto3"; enum Command { UNKNOWN_COMMAND= 0; FILER_INITIALIZE = 1; FILER_RELOAD_ALL= 2; FILER_MOVE_LOCATION= 3; FILER_UPDATED= 4; FILER_COPY_INTERACTION= 5; FILER_MOVE_INTERACTION= 6; FILER_DELETE_INTERACTION = 7; KEYMAP_ADD_KEY_BINDING= 8; KEYMAP_REMOVE_KEY_BINDING= 9; KEYMAP_GET = 10; KEYMAP_UPDATED = 11; } // common request message Request { string id = 1; Command command = 2; bytes payload = 3; } enum Status { UNKNOWN = 0; SUCCESS = 1; INVALID_REQUEST_PAYLOAD = 2; COMMAND_FAILED = 3; } message Error { int32 status = 1; string error_message = 2; } // common response. Field `id' must same value of the request. message Response { string id = 1; Status status = 2; bytes payload = 3; Error error = 4; } message SomeProcedureRequest { string fooBar = 0; } message SomeProcedureResponse { int32 count = 0; } service SampleService { rpc someProcedure(Request) returns (Response); } ポイントはいくつかありますが、特に大事だと感じたのは次の点です。 ...

March 15, 2020 · derui

hygen.ioでboilerplateを自動生成すると捗る話

閏年の閏日ということなので(?)、記事を書いておきます。特別な日にでも書いておかないとアウトプットがないので・・・。 今回は、最近使い始めて結構いい感じになってきた、hygen.ioについてです。 <!–more–> hygen.ioとは hygen.ioは、公式で以下のように紹介されています。 The scalable code generator that saves you time. 簡単に書くと、MavenとかGradleとかで初期構成を自動生成したり、create-react-appとかで生成したりといった、code generatorの一つです。 特徴としては 速度 と シンプルである ことで、複雑なDSLを覚える必要は特になく、簡単に使い始められます。また、後述する inject という機能のおかげで、自動生成しつつ、その情報を別ファイルに埋め込む、みたいなことが割と簡単です。 どんなprojectで使われてる? ここを見ると大体わかりそうです。JavaScript界隈での有名企業が入っていたりと、それなりに広く使われているようです。 なお、gulpとかnpm scriptとかMakefileでも出来るんちゃう?という気もしますし、実際出来ると思いますが、code generatorとして特化した機能を提供しているhygenを利用する方が、設定のごった煮になる可能性が低いかな・・・という気がします。 boilerplateを自動生成してみる 今個人で作業しているリポジトリでは、Reduxをmoduleという形で利用するとともに、多数のcommandというmoduleを生成する必要があります。ほとんどinterfaceだけは決まっているので、新しいcommandやmoduleを追加する度、同じようなファイルを生成したり、構造に気を使ったり・・・という作業が必要になります。 流石にこれはめんどくさい・・・となってきたので、hygenを利用していろいろ自動生成してみました。 hygen自体の使い方は公式サイトを見てもらったほうが良いと思いますので、リンクだけ貼っておきます。今回作ったgeneratorの構造はこんな感じです。 実際に使っているのはもうちょっと色々追加されています。 --+ _template |-+ module |-- help |-+ init | |-- actions-test.ejs.t | |-- actions.ejs.t | |-- index.ejs.t | |-- inject_reducer.ejs.t | |-- inject_import-module.ejs.t | |-- inject_action-type.ejs.t | |-- reducer-test.ejs.t | |-- reducer.ejs.t | |-- types.ejs.t |-- new-action これを使うと、こんな感じで新しいmoduleを追加したり、追加したmoduleに対して新しいactionを追加したり出来ます。 ...

February 29, 2020 · derui

polybarからi3blocksに乗り換えてみた

今年の冬は本当に暖冬で、今から今年の夏が心配です。水不足的な意味で。 時事ネタは置いといて、最近デスクトップのbarをpolybarからi3blockに移行したので、なんで移行したのかとかを書いておこうかと思います。 <!–more–> i3blocks i3blocksは、その説明にあるとおり、text-baseなstatus barです。polybarもだいたい同じような感じですね。これらの違いを個人的にあげてみます。 polybar feature rich 設定ファイルでだいたい何でもやる 組み込みで結構な量のmoduleがある i3blocks 本体機能は必要最小限 組み込みのmoduleはほぼ無い i3blocksは、組み込みmoduleとかがほぼない代わりに、moduleから返された結果を表示するだけ、というシンプルな形式に割り切っています。そのため、複雑化しやすい(個人の意見です)polybarよりも設定ファイルがシンプルになります。 polybarから乗り換えた理由 polybarは、その組み込みmoduleが多いことも相まって、コンパイル時の依存が多いです。出来るだけ依存を少なくしたいのと、利用しないmoduleが多かったというのもあり、他のstatus barを検討していました。 i3blocksは1.4まではデフォルトでmoduleを用意していましたが、1.5で大きく変更され、i3blocks本体は最小限で、moduleは全てi3blocks-contribに分割されています。(blockletと読んでいるようです)。このため、必要なものを自分でさっくり作るなり、必要なscriptだけを取得するとかが簡単です。 また、色とかもmodule内で出力することで個別に変更できるため、別ファイルにまとめておいて読み出す、とかもできます。configにまとめるのとどっちがいいか、というのはありますが・・・。 i3blocksの設定 color=#8fa1b3 [title] command=~/.config/i3blocks/scripts/title.sh interval=persist [uptime] label= command=uptime | sed 's/.*up \([^,]*\),.*/\1/' interval=60 [memory] label= command=~/.config/i3blocks/scripts/memory.sh interval=1 [load average] label= command=echo "$(uptime | sed 's/.*load average: \(.*\)/\1/' | cut -d, -f1)/$(grep 'processor' /proc/cpuinfo | wc -l)" interval=1 [date] label= command=echo " $(date '+%Y/%m/%d %H:%M(%a)')" interval=1 [power] label= command=~/.config/i3blocks/scripts/power.sh interval=persist [separator] 実際に使っているconfigです。polybarと違い、各moduleの設定は同一で、違うのはcommandとかintervalの中身だけ、という統一感がいい感じです。 ...

February 21, 2020 · derui

Domain Modeling Made Functionalを読んだ

個人的に作っているツールで、OCamlでどうやってDDDをやっていくか?ということを考える中で、 Domain Modeling Made Functionalというそのものズバリな本の存在を知りました。そこまで高くなかったので購入して読んでみたので、感想を書いてみます。 <iframe style=“width:120px;height:240px;” marginwidth=“0” marginheight=“0” scrolling=“no” frameborder=“0” src="//rcm-fe.amazon-adsystem.com/e/cm?lt1=_blank&bc1=000000&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=derui09-22&language=ja_JP&o=9&p=8&l=as4&m=amazon&f=ifr&ref=as_ss_li_til&asins=1680502549&linkId=05192cc54dff2d67c58d290cad5cdd28"></iframe> <!–more–> どんな内容? すごい簡単に書くと、 F#でDDDをやっていく時のノウハウが詰まっている 本です。たいていこういう本はScalaとかHaskellで書かれている印象(偏見)なので、F#というのが中々ニッチな印象でした。 ちなみにF#を知らない方のために紹介だけしておくと、F#は以下のような特徴を持つ言語です。 OCamlをベースにした関数型言語 ベースにしているので、命名規則とか文法とかは違いますが、ML族です なので、型クラスとかはありません .NET Platform上で動く 多分.NET Coreでも動くんではないでしょうか OCamlを使っている人間としては、F#の文法は若干の違和感を感じるくらいで、特に読みづらさとかは感じませんでした。 もうちょっと細かい内容 概ね、以下順で進んでいきます。 DDD自体の解説 仮想プロジェクトを使ったDomain導出の流れ この部分が、対話形式になっていてなかなか面白いです。また、ダイアグラムなどをあえて使わず、擬似言語を用いてユビキタス言語やビジネスの制約とかを書き下しているのが印象的でした。やってみたい ドメインをどうやって型に翻訳していくか ここからが関数型言語(特に代数的データ型を持つ言語)でどうやってドメインを型にしていくか、という話題です。この時点では実装を一切考えず、ビジネス要件を型の表現力でどう表現するか?に注力しています。 ワークフローをどう表現するか ビジネス上のワークフローを、小さいstepという関数で表現していくか、という内容です。ここでも実装そのものは行わず、step/work flowをひたすら型で表現していきます。 型に対する実装 ドメイン自体、そしてワークフローに対して行った大量の型をどのようにつなぎ合わせていくか、という内容です。ここから実装が登場します。バリデーションやエラーを扱う話題もあります。 関数でワークフローを表現した時、stepの依存などをどのように扱うか、という内容もあります。関数適用をDependency Injectionとして利用するなど、関数型言語で一般的なテクニックなども紹介しています。 エラー実装、永続化、シリアライズなど現実的な内容それぞれ独立した章に分かれていますが、全て実装に関する内容です。 エラーでは、主にResultをどう扱うか、Resultをどう繋げていくか、といった実践的な内容となっています。永続化、シリアライズでは、DBやJSONへのシリアライズなど、主にWebアプリケーションで扱いそうな内容を多く扱っています。 特に印象に残った点 DDDに当たる部分は、Evans本やIDDD本を読んでいれば、ある程度は読み飛ばしてしまっていいと思います。そこ以外で印象に残っていたり、参考になったものがいくつかあります。 とにかく型で表現する 文中には、必要に応じて減らしてもいい、という書き方をしています しかし、step/work flowすら型で表現する、というのか魅力的です IDとかは、実際にはfunctorで作ったり、ある程度自動的に導出することも出来るので、方はある程度多めになってもなんとかなる気はします Monadは必ずしも必要ではない 実際、文中ではMonadという言葉をほとんど使っていません 言及している部分では、 それほど恐れる必要はない という記述になっています Free Monadなどにも触れているので、実際のアプリケーションなどでは使うかもね・・・というニュアンスなのかもしれません 関数適用はDI 最近オブジェクト指向言語ばかりやっているのと、部分適用して使う、というのが普通すぎて、逆に目からウロコでした IOはEdgeに追いやる DomainはIOを知るべきではない、というのを何度も書いています Clean Architecture/Onion Architecture/Hexagonal Architectureといったアーキテクチャをより簡潔に言い表したものだなーって思います Edgeにどうやって追いやる?関数を使えよ、という当たり前の内容もちゃんと書いてくれています 最近OCamlで書いていると、なんとなくFunctorを使ってしまう部分でも、より基本的な関数をまず使おう、と思い直しました DTOをきっちり使う重要さ Domainを直接JSONなどに変換してはならない理由をちゃんと説明している点が非常に良かったです 個人的にもDomainをそのままAPIなどに露出しないようにしていますが、次からは何故そうするのか?と説得できそうな気がします 現実だと工数がかかりすぎる、とか言われそうですが・・・ 型パズルの解き方 大量の型が出てきた時に、どのように関数を繋げていくか、という方法論が書かれています 関数型言語でもDDDをやりたい人にはオススメです DDDをJavaとかC#、他の言語ではやっているけど、関数型言語ではどうやるんだろう、Monadとかよくわからない概念のオンパレードになるんじゃないか、とか思っている人にオススメです。 ...

February 7, 2020 · derui

Protocol BuffersとJSONの扱い in OCaml

以前の記事で、ProtocolBuffersでOCaml/TypeScript間の定義を作成して色々やっていたのですが、いくつか問題が出てきたので書いてみます。 <!–more–> ocaml-protoc-pluginに乗り換えた 前回は ocaml-protocを使いましたが、ocaml-protoc-pluginに乗り換えました。理由としては以下となります。 標準のprotocにおけるpluginという形での提供 自前で解析していてもいいとは思いますが、他のproductではprotocのplugin形式が多かったので 型定義にannotationを付けられる yojsonとかに変換するのが簡単です ts-protoc-gen + protoc標準から、pbjs(protobufjs)に乗り換えた 前はprotocに標準添付されているJavaScript実装と、ts-protoc-genを組み合わせていたのですが、以下のような問題があったので乗り換えました。 Jsonからの変換とかが出来ない protocの標準では、 protocolbuffers以外に対するserialize/deserializeがありません JSON-RPCを利用しているので、軽く絶望しながら切り替えました(先に調べろと 発生した問題 今作っているapplicationでは、Electronとbackend server間をWebsocketでつなぎ、RPCとしてJSON-RPCを利用しています。この構成、面倒ではありますが、割と使い勝手がよいのです。しかし、protoファイルから生成した型を使っていると、困る割にいい解決策が無い問題にあたりました。 その問題とは、 OCamlでの型定義とProtocolBufferでの型定義との互換性 です。ナンノコッチャですが、要はocaml-protoc-pluginが生成してくれる型と、protocol_conv_jsonのでのJSON変換が一筋縄では行かない、ということです。 私は大きく2つの問題にあたりましたが、そのうちの一つを例に上げます。 enum Types { Unknown = 0; String = 1; Number = 2; } message Foo { Types value = 0 } こんな感じのprotoファイルからOCamlの定義を作成すると、以下のような感じになります(moduleの定義は省略しています)。 open Ocaml_protoc_plugin.Runtime [@@warning "-33"] module rec Types : sig type t = Unknown | String | Number val to_int: t -> int val from_int: int -> (t, [> Runtime'.Result.error]) result end and Foo : sig val name': unit -> string type t = Types.t val to_proto: t -> Runtime'.Writer.t val from_proto: Runtime'.Reader.t -> (t, [> Runtime'.Result.error]) result end ここでポイントとなるのが、protoファイルにおいてenumと定義した部分と、OCaml版におけるTypes moduleです。OCamlにおいては、Enumを代数的データ型として表すのはごく自然だと思うのですが、問題はProtocol Buffersにおいては、enumは 単なる数値 でしかありません。 to_int とかがそれを物語っています。 ...

January 13, 2020 · derui