TryFSharpとその技術スタック

今は昔、Web上で簡単にF#を試せるTry FSharpはSilverlight上で動作していました。 しかし世界から光が失われた結果、賢者たちは世界の再構築を余儀なくされたのでした。

Try F#

というわけでTry FSharpは今もFSharp Software Foundation下で開発、運営されていてしかもモダンなもので構築されているよという話です。 どのくらいモダンかといえば

聞き覚えのあるものばかりだと思います。 これくらい道具がそろっているとそこそこのウェブサイトであればフルF#で開発できるかも。 いい時代になりましたね。

ちなみにこのTry FSharpはソースコードが公開されています。 興味のある方はぜひ眺めてみましょう。

GitHub - fsharp/TryFSharp

FableやElmishの話を書き始めるとちょいと長くなりそうなので、この記事では触れずに終わります。 中身がない記事でごめんね、Try FSharpを紹介したかっただけなのさ!

ところで、某FSDNをフル F# でリプレースしようと画策しているのですが、果たしてうまくいくのでしょうか…? うまくいくといいなぁ(未来の自分に期待、そしてフラグ)

Microsoft MVP for Developer Technologiesを再受賞しました

今年も受賞できました7回目。関係各位の皆様ありがとうございます(特に前職ドワンゴ)。 ちなみに今年も活動報告は F# オンリーでした。

ここ数年は F# の新機能が小粒揃いだし、仕事は相変わらず .NET 関係ないしで悶々としていましたが、ようやくメジャーバージョンアップの足音が聞こえてきたのでまた1年頑張っていこうと思います。 リポジトリ.NET Foundation下に移ったことですし、気持ちを新たにやっていく所存です。

まぁ、そのまえにまずはFSDNの全面改修頑張りますはい。。。

というわけで、今後ともよろしくお願いします。

F# 4.6の話

忙しくて書く余裕がなかったF# 4.6についてです。といっても機能追加は小粒なのでさくっといきましょう。 間違ってたら指摘をお願いします。

ちなみにこの記事を書いている時点における最新の FSharp.Core は 4.6.2です。

FS-1030

https://github.com/fsharp/fslang-design/blob/7240c64de3e85f69de872b1f0a7db4d91f7a7e1a/FSharp-4.6/FS-1030-anonymous-records.md

anonymous recordsはC#に存在するanonymous objectsのレコード版ですね。 これまでASP.NET CoreなどのC#フレームワークをF#で触ったりするとanonymous objectsが前提となっていた部分が煩雑でしたが、これでいい感じに書けるようになります。

FS-1065

https://github.com/fsharp/fslang-design/blob/7240c64de3e85f69de872b1f0a7db4d91f7a7e1a/FSharp.Core-4.6.0/FS-1065-valueoption-parity.md

4.5で導入されたvalue option用の関数やメソッドが追加されます(なぜ4.5で入らなかったのと突っ込んではいけない)。 あとDebuggerDisplay属性も付与されるようです。

FS-1066

https://github.com/fsharp/fslang-design/blob/7240c64de3e85f69de872b1f0a7db4d91f7a7e1a/FSharp.Core-4.6.0/FS-1066-tryExactlyOne.md

array, seq, listにtryExactlyOne関数が追加されます。 コレクション中に要素が1つしかなかった場合にその要素をとりだすexactlyOne関数のoption版ですね。

おわりに

マイナーバージョンアップなのでこんなものでしょうと思うべきか、もうちょっと頑張ってほしいと思うべきか…それはともかくとして、ちゃんと改善はされていってるのは良い事だでしょう。 RFCsフォルダに未だリリースされていない仕様がたまってきているのでそろそろリリースされないかなぁ。

gRPC C# のHealthCheck

この記事は.NET, .NET Core, monoのランタイム・フレームワーク・ライブラリ Advent Calendar 2018 - Qiita 25日目の記事です。 ほんとはGiraffeの話を書こうと思っていたけど、仕込む時間がなかったので別ネタで行きます…!

あと時間がないので色々と飛ばして書きます。

gRPCのhealth checkプロトコル

https://github.com/grpc/grpc/blob/v1.17.2/doc/health-checking.md にあるとおり、gRPCにはhealth checkプロトコルが用意されています。 これを使って例えば:

  • サーバー側でヘルスチェックに応答できるようにしておく
  • クライアントにhealthcheckコマンドを用意する
  • KubernetesのLiveness Probeでhealth checkコマンドを叩く

とかできたりします。

ライブラリインストール

公式が提供しているライブラリを使います。

https://www.nuget.org/packages/Grpc.HealthCheck/

サーバー側

使う名前空間は以下:

using Grpc.Core;
using Grpc.Health.V1;
using Grpc.HealthCheck;

他のサービスと同様にHealthServiceをサーバーに登録し、サーバー起動後にSetStatusで状態を設定してあげます。 第1引数はサービス名でいいはず。

var health = new HealthServiceImpl();
var server = new Server
{
    Services = {
        Health.BindService(health)
    },
    Ports = { new ServerPort("localhost", 50051, ServerCredentials.Insecure) }
};
server.Start();
health.SetStatus("greeter", HealthCheckResponse.Types.ServingStatus.Serving);

これで、クライアントから起動しているか確認できるようにはるはず。

クライアント側

使う名前空間は以下:

using Grpc.Core;
using Grpc.Health.V1;

クライアントはレスポンスから状態を取得して分岐させます。

Channel channel = new Channel("localhost:50051", ChannelCredentials.Insecure);
try
{
    var client = new Health.HealthClient(channel);
    var response = client.Check(new HealthCheckRequest { Service = "fanfun" });
    switch (response.Status)
    {
        case HealthCheckResponse.Types.ServingStatus.Serving:
            // 何か処理
            return;
        case HealthCheckResponse.Types.ServingStatus.NotServing:
            // 何か処理
            return;
        default:
            // v1だと他の状態はUnknownのみ
            // 何か処理
            return;
    }
} catch(Exception e) {
    // 略
}

あとは実際にサーバーを起動しつつクライアントを実行すれば結果が確認できるはず。

ところで

MagicOnionにはPingとHeartbeatサービスが組み込みで用意されています(このあたり)。 これ以外にも便利な機能があるので C# に統一できるならこっちのほうがいいかもしれません。

私は grpc-web とセットで使いたいのでMagicOnionは使えないですけどね!

commandpostのBuckleScript bindingを書いた

これは元ドワンゴ Advent Calendarの22日目の記事です……遅刻しましたけど22日ということで。

今年に入ってから、私はBuckleScriptで自作ノベルゲームエンジン用のトランスパイラhttps://github.com/cowlick/cowlick-sexpr-compilerを開発しています(最近さぼってたけどぼちぼち再開予定)。 このツールを開発する際に、option parserとしてこれまでは https://github.com/jaredly/minimist.reを使用していました。 しかし、このライブラリには同じオプション名が複数回指定されると配列で受け取る、といったAPIが用意されていないので最近機能追加をしようとしたタイミングで困ってしまいました。

さて、では他のライブラリを……使おうにも選択肢がないです。 このあたりにユーザー数の問題が現れますねぇ。

ではしょうがないので自作か、という話になりますが、それよりは既存のライブラリをbindingしてしまえという話に落ち着くので書いてしましましょう。 今回は個人的によく使うhttps://github.com/vvakame/commandpostを選択しました。

成果物

publish済みです。

https://www.npmjs.com/package/@pocketberserker/bs-commandpost https://github.com/pocketberserker/bs-commandpost

方針

今回はCommandクラスとcreate関数、exec関数に型をつけました。 ArgumentOptionは使う機会がなさそうなのでひとまず後回しにしています。

Commandクラスのラップ方針はexternalbs.sendを使ってモジュールに関数を定義する形にしました。 Command型は('a, 'b) tで定義し、第1引数にはこのオブジェクトを渡すようにします。 例えば、create関数とdescriptionメソッドの定義はこんな感じになります。

module Command = struct
  type ('a, 'b) t

  external description: ('a, 'b) t -> string -> ('a, 'b) t = "" [@@bs.send]
  (* 色々略 *)
end

external create: string -> ('a, 'b) Command.t = "Command" [@@bs.new] [@@bs.module "commandpost"]

bs.newはコンストラクタ呼び出しであることを示します。 bs.moduleを指定するとその関数を呼び出すときに記述したモジュールがrequireされます。

BuckleScriptは|.で第1引数をパイプラインの対象にできるので、第1引数を対象のオブジェクトにしていてもそんなに困ることはないです。

create "test"
|. Command.description "test"

パイプライン便利。

型の話

悩んだ点がふたつあります。 ひとつはコンストラクタの型パラメータCommand<Opt, Arg>、もうひとつはanyです。

型パラメータは、今回に関してはletで束縛する際に明示して型をあわせる方向でなんとかしました。 これがあっているのかはちょっとわかりませんが…。

type root_options = RootOptions
type root_args = RootArgs

let root: (root_options, root_args) Command.t = create "root"

anyについてはてきとーに型があえばいいので使ってない型パラメータを指定する方向で調整しました。

まとめ

BuckleScriptでは比較的簡単にbindingが書けます。 というわけで皆さんもbindingに挑戦してみましょう。

転職します

お世話になりました & ありがとうございました。 今後ともよろしくお願いします。

*1:10月はほぼ有給休暇を消化してました

SpanJson用の F# 拡張ライブラリを作った

SpanJson という新手のJSONリアライザが今年になって登場していたので、調査がてらに F# 拡張を作りました。

https://github.com/pocketberserker/SpanJson.FSharp.Formatter

https://www.nuget.org/packages/SpanJson.FSharp.Formatter/0.1.0

SpanJson自体が.NETCoreApp 2.1でしか使えない上に F# 向けライブラリなので使う人皆無な気がしていますが、SpanJsonに詳しくなったのでよしとしましょう。

0.1.0で対応しているのは以下の型です。

  • option
  • voption
  • list
  • map
  • record
  • struct record

setは既存実装のままでシリアライズ、デシリアライズできるので自前で用意しませんでした。 判別共用体は例によって必要性がまだわかないので未対応です。 あとはvoption対応のために FSharp.Core を 4.5以上に強制しています、各位新しいバージョンを使っていきましょう。

SpanJsonはUtf8Jsonと似ているようでところどころ違うのが面白いですね、個人的にはUtf8JsonのAPIのほうが好み(というか単純に実装しやすい)と思いましたが。 Resolverの伝搬を型パラメータで行うの結構大変な気がするのですがそこらへんどうなんでしょう…?

あとはSpanJsonでクラスや構造体をシリアライズ、デシリアライズする際はデフォルトかattributeを付けたコンストラクタでなければならない縛りはちょっと地味に厳しいかも? これの関係でrecordはシリアライズできるけどデシリアライズできないという状態になったので、外部ライブラリの型をシリアライズしようとするときに色々書く羽目になるかもしれません。

そういえばこのライブラリを開発する際に C# 7.3 を使ったのですがout varとか便利ですね…という気持ちになりました。