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とか便利ですね…という気持ちになりました。

Akashic Engineでガチャが回せるゲームのサンプルを作ってみた

とある個人的な(?)イベントのためにガチャつきアドベンチャーゲームもどきを実装しよう、と思い至ったので作ってみましたというメモ書きです。 下記はその残滓(公開できない & したくないものを排除しシナリオを簡易化したもの)。

https://github.com/pocketberserker/rgo

ちょっとしたシナリオとチュートリアルガチャと本番ガチャをまわすだけのゲーム(?)です。 画像とフォントを揃えれば試せると思います。 ライセンスはちょっと決めかねているのですがそのうち…。

使ったもの

Akashic Engineについては下記記事を一読ください。

https://dwango.github.io/articles/akashic/

ノベルゲームエンジンについては昨年末にQiitaに記事を書きました。

https://qiita.com/pocketberserker/items/a14c4358adff46ee9742

あとは抽選にwalkers alias methodを使おうと思い、どうせだからAkashic Engine向けライブラリにしておこうと考えたので(誰が使うんだ?)作って組み込んだ感じです。

https://github.com/pocketberserker/akashic-random

今回のゲームはクライアントのみの実装です。 サーバーも実装しようかと企んでいたのですが、猶予が3週間弱くらいしかなかったので泣く泣く断念……とはいえ当日のイベントでは笑ってもらえたのでなんとかなってよかった。

追加したかったけど無理だったもの

  • n連の実装
    • 演出の都合により途中であきらめた
  • キャラクター情報閲覧機能
    • UI作る余裕が…

空き時間をみつけて実装したいところですはい。

F# 向けテスティングフレームワークPersimmonのv4系リリース

だいたい作業し終えたので告知です。

https://www.nuget.org/packages/Persimmon/4.0.2

https://www.nuget.org/packages/Persimmon.TestAdapter/0.11.0

https://www.nuget.org/packages/Persimmon.Dried/4.0.1

https://www.nuget.org/packages/Persimmon.MuscleAssert/3.0.0

https://www.nuget.org/packages/Persimmon.Script/4.0.0

https://www.nuget.org/packages/Persimmon.Unquote/0.2.0

paket.templateに慣れていなくてpatch versionが少しだけ上がってます…。

例外として、PersimmockはPersimmon非依存なので特にアップデートはありません。

Fake.Persimmonについては後述します。

netstandard1.6 or netstandard2.0サポート

上記で紹介したライブラリはどちらかに対応しています。 ようやくスタートラインに立てた…。

FSharp.Core 4.5.xが利用可能

ユーザ側はFSharp.Coreのバージョンを気にしなくて良くなったので*1新しめのFSharp.Coreでも動くはずです。

これはだいたい id:htid46 さんのおかげです、ありがとうございました。

net40以下 & PCLドロップ

FSharp.Core 4.2.x以降サポートされなくなったframeworkをサポートから外しました。 もしこれらの環境で使いたい場合はPersimmon 3系以前を使ってください。

Fake.Persimmon

FAKE 5で命名規約がかなり変わったこともあってdeprecatedです。

もし使いたいなら id:htid46 さんがgistに試作品を用意してくださったのでこれを使ってください。

https://gist.github.com/hafuu/ea5b0140d01beb97f10ed20079cdcf72

今後の予定

ドキュメントが散らばりがちなのでgithub.ioに作る予定です。 というかFSharp.Formattingがもう厳しい…。

あとPersimmon.Templatesはそのうち更新します。

*1:今まで無理だったのかよ、というツッコミは…ごめんなさい