F# API検索サービス"FSDN"を作りました
公開から少し間が空きましたが、改めて周知ということで。
http://fsdn.azurewebsites.net/
このサービスは現在Azureで運用しています。 運用にあたり、株式会社オンザロード様にスポンサーについていただきました。
私がメンテナンスしていく限りはUNIX、Linux環境でも動くようにしておくつもりです。 ただ、メンテナが変われば方針が変わるかもしれません。
検索対象ライブラリ、アセンブリ追加要望は https://github.com/fsdn-projects/FSDN/issues でお願いします。
FSharpApiSearchにべったり依存しているので、機能によってはそちらを先に拡張しないとFSDN側ではどうにもならない可能性が大きいのです。
技術的なお話はまた今度。
Babelの勉強と称してmuscle-assertを作ってみる
私は型がないと死んでしまう(コンパイラと相談しないと考慮漏れが多発して死ぬ)ので、JS系を触る場合は主にTypeScriptなのですが、とはいえ昨今の事情的にBabelを食わず嫌いするのなぁ…と思ったのでライブラリとそれ用のpuluginを作ってみることにした。
勉強前のスキル
- Babel系の情報は追っていたが使うのは(ほぼ)始めて
- 既に別の人が構築したものをちょろっと触ったことならある
- TypeScriptは使える(詳しいわけではない)
方針
こういうのは強い方々の力を借りるに限る。
https://github.com/azu/power-assert-as-tool-demo
Babel pluginの知識は https://github.com/thejameskyle/babel-handbook を斜め読みしつつ、https://github.com/power-assert-js 下にあるbabel pluginを読み漁った。
作るついでに類似品チェック
Persimmon用アサーションライブラリMuscleAssertを作った - pocketberserkerの爆走
この記事の記憶がまだ自分の脳内に残っているうちに移植してみたかったので、今回はこれを題材にした。 一応類似品を紹介しておく。
https://www.npmjs.com/package/assert-diff
やっていることはだいたい同じだが、標準assertをベースにしたかったので自作しようという心持ちである。
あと、似たようなdiff表示はmocha
であれば行ってくれる。
だからmochaであれば必要ない…のだが、eaterなどではそういうわけにもいかないと思う。
成果物?
https://github.com/pocketberserker/muscle-assert https://github.com/pocketberserker/babel-plugin-muscle-assert https://github.com/pocketberserker/muscle-assert-demo
Babel初で一日少々にしては私的には頑張ったほうじゃないですね…まぁ、参考にできるものがおおかったからだろうけど。
muscle-assertは標準のassert.deepStrictEqual
のみを置き換える方針にした。
こうしたほうがシンプルだろうし、他のライブラリとも干渉しなくなるだろうと思った。
そういうわけでBabel plugin側も最低限の置き換えにとどめている。
使い方はデモプロジェクトをみればわかるはず。 表示がしょぼいのはプロトタイプなので勘弁してください。 バグっていたらそのうち直します。
ドキュメントがなさすぎるのは…本気で実装する気力が起きれば書く。
課題
なるべくpower-assertと干渉しないように作りたい…と思っていたのだが、以下の問題にぶつかった。
@pocketberserker babel-plugin-espower は Babel 黒魔術を使って他の全ての plugin に先回りして動作するからです
— Takuto Wada (@t_wada) 2016年6月6日
babel-plugin-espowerが先に変更してしまうのでテストがおかしくなる。
回避方法は今のところbabel-plugin-espowerのpatterns
にassert.deepStrictEqual
をいれないという場当たり的なもの…なんとかしたいが、なんとかできるのかなぁ。
というわけで、今のところbabel-preset-power-assertとの共存は難しいのではないかと考えている。
おわりに
Babelは入門するだけならそんなに時間がかからないようだ。 power-assertの導入は記事が乱立しすぎているし古い情報がノイズになってTypeScriptのときはつらい気持ちになっていたが、Babel前提だとbabel-preset-power-assertがよろしくやってくれるので良いと思う。 とはいえ、楽になったのはあくまでBabelマンだけなのでTypeScriptの時にもう少しどうにかならないか考えてみたくもある。
babel-plugin-espowerのあれはやりたいことを考えると仕方がないのだと思う。 こういうのは後発ライブラリが頭をひねれば良い問題だと思うので、babel-plugin-espwerさんはそのままの強さで居てほしい。
Persimmon用アサーションライブラリMuscleAssertを作った
正確には「作っていたライブラリをPersimmon.MuscleAssertにrenameした」です。
注意
この記事はあくまで私の考えでありpersimmon-projectsの総意であるわけではありません。
前提
- PersimmonはF#用のテスティングフレームワークです https://github.com/persimmon-projects/Persimmon
そんなにF#寄りの話はしないはずなのでこれくらいの情報量で大丈夫なはず…?
Persimmonはpower assertの夢を見るか?
昨今はpower assertの認知度が格段に上がっているようですね。 これはJavaScript向けのpower-assertによるものが大きいのではないかと思われます。
ちょっと脱線しつつPersimmonにpower assertが必要か考えてみましょう。
どの意図でのpowert assert?
assertという言葉は二つの用途で使われいると思います。
- DbCとしての表明(assertion)
- checkingを目的としたassert関数
DbC向けの用途でpower assertが利用されることはまだ少ないようです。 たぶんGroovyとJavaScript*1くらいなのではという認識です。 私が知らないだけかもしれないのですが…。
一方、ユニットテストフレームワークの文脈上で登場するpower assertは様々な言語に存在します。 .NETにもhttps://github.com/PowerAssert/PowerAssert.Netが存在します。 使い勝手は触ったことがないのでよく知りません…。
今回は後者のほうでのpower assertについての言及です。
我々が欲しかったものはpower assertなのか
かつて、下記の記事が界隈をにぎわせました。
そろそろPower Assertについてひとこと言っておくか - ぐるぐる~
状況が少し変わっているのは
- Groovy(こっちは当時から?)やJavaScriptのpower assertでは文字列の比較で差分を表示する
あたりでしょうか。 あとは下記も参照すると良いかもしれません。
https://github.com/persimmon-projects/Persimmon/issues/4#issuecomment-60347183
これにF#的な補足をしておくと
- オブジェクトの一致を確認することが多い
- 判別共用体やレコードなど型を作りやすい環境にあるため、文字列や数値などではなくプロパティを持ったオブジェクトを比較することになりやすい
- パイプライン演算子でつなげるスタイルで結果をassert関数に渡す場合もある
- Elixirのように強力なマクロがあるわけではないので途中の式が解析対象にできない
- power assertのためにスタイルを変えるのか?的な
みたいな話があるようなないような感じです。
プロパティを幾つも持つオブジェクト(例えばJSONオブジェクトをマッピングしたレコード)をpower assertにかけると無駄に情報過多になりやすく、しかしinlineに式を書かないことのほうが多いため必要な情報が増えるかどうかは微妙なところです。 情報が増えないのにグラフィカルな失敗結果が表示されても嬉しいとは思えません。
はたしてこの状況で、苦労してpower assertを実装する必要があるのでしょうか?
少なくとも私には手を出す気力がおきませんでした*2。
力技で不要な情報を握りつぶす
しかし、やはり単純なassert関数は力不足です。
// https://github.com/persimmon-projects/Persimmon/blob/cdba2509e5d48e4cdd47f292f3578b00371c85fc/src/Persimmon/Assertions.fs#L9 let assertEquals expected actual = if expected = actual then pass () else fail (sprintf "Expect: %A\nActual: %A" expected actual)
上記はPersimmon 1.1.0のassertEquals
ですが、とにかく貧弱です。
情報量がsprintf
で出力可能な範囲に絞られるわけですが、こいつの表示はあまりあてにできないと思ったほうが良いです。
ひどいときには型名しか表示されません(それでも言語組み込みのassertよりはマシですが)。
これではさすがにつらい(らしい)のでもうちょっと情報量増やせないか、という話になるのは必然です。 しかしpower assertには懐疑的…どうしようという話になります。
さて、id:bleis-tiftさんの記事ではこう述べられていました。
ユニットテストにおいて、最も欲しいのは「どこがどうなっているか」ではなく、 「どこがどう違っているか」じゃないですかね。
これをPersimmon開発チームでもう少し協議した結果、「オブジェクトのdiffをだそう」という流れになりました。 というわけで出来上がった試作ライブラリがPersimmon.MuscleAssertです。
https://github.com/persimmon-projects/Persimmon.MuscleAssert
このライブラリができることは「等値比較し、不一致だったらオブジェクトの差分をひたすら表示する」ただ一つです。 とはいえ、実際には以下の制約がつきます。
System.Type
はアクセスした瞬間に例外が飛ぶプロパティもリフレクションで取れてしまうためFullName
プロパティのみ対象にする- IEnumerableはdiffの対象にしないで「無視したよ」と警告をだす
- 無限リストのである可能性や、iterateする際に副作用が発生しオブジェクトが操作されてしまう可能性があるため
- stringも未加工で表示
- 今後の方針次第ではdiff-match-patchを導入するかもしれません
コード例と実行結果を見てみましょう。
open Persimmon open UseTestNameByReflection open Persimmon.MuscleAssert type TestRecord = { X: string list Y: int } type TestDU = | A | B of TestRecord | C let ``dump diff list includeing DU`` = parameterize { source [ ([A; A], [A; C]) ([A], [B { X = []; Y = 2 }]) ([B { X = []; Y = 1 }], [B {X = []; Y = 2 }]) ] run (fun (expected, actual) -> test { do! expected === actual }) }
テスト結果では以下のような表示が垣間見えるはずです。
Assertion violated ... 1. .[1] left TestDU.A right TestDU.C Assertion violated ... 1. .[0] left TestDU.A right TestDU.B Assertion violated ... 1. .[0].Item.Y left 1 right 2
値が異なる部分のみに表示を握り潰す、実に脳筋ですね。
命名理由
「普通のアサーションと区別しにくいから名前をつけよう」という話になった際に
Power Assertは時代遅れ、今はMuscle Assertだ!的な話かな?
— ガブさん肉 (@gab_km) June 1, 2016
これがなぜかそのまま採用されました。
power assertと対立する存在なのか?
不明です。
正直なところ、言語によってはpower assertのほうが使い勝手が良い可能性はもありますし、各種power assertライブラリがオブジェクトdiff機能を取り込めば話は変わってくるかもしれません。
とはいえ、一つだけ明確に言える違いがあります。 それはdiffを取得するだけであればASTの操作は必要ないという点です。 世の中にはテストでASTを操作することに懐疑的な派閥もあるらしいですが、そういった派閥にも受け入れられる可能性は残されています。
どんな言語でも実装できるのか
オブジェクト用diffライブラリはJavaから移植しました。
そのライブラリではget
プレフィックスのついたメソッドを対象としていたので、何かしらの制約(getterのような慣習的なもの)を設ければ実装できるのではないでしょうか。
おわりに
ユニットテストフレームワークには以下の方針や理想(?)があると思っています。
- 成功時は沈黙し、失敗時はやかましく
- 「なぜ失敗したのか」を的確に伝える
- もしくは容易に推論可能な形で表示する
power assertがこのまま勢いを増すのか、筋肉式assertの流れが来るのか、第三の概念がかっさらうのかはわかりません。 ただ日々考え、実装し、議論を交わさなければ停滞するだけだと思います。
これを機会に一度、「テスト失敗時に欲しかった情報とは何か」を考えてみるのも良いかもしれません。
*1:https://github.com/power-assert-js にあるツール群を駆使すれば可能という話を聞きかじっただけなので、詳細はその方面の方に尋ねてください
*2:テストケースの構文自体を解析対象にしようと試みたことはありますが、これも情報過多になりそうだったので一旦開発を止めています https://github.com/persimmon-projects/Persimmon.Pudding
diff-match-patchを F# に移植してみた
世の中にはdiff-match-patchと呼ばれる、Google製の便利な文字列diffライブラリが存在します。
そんなdiff-match-patchですが、C#版はコードは存在するもののnugetにpublishされていません。 また、コードは.NET Framework 3.5以上である必要があるため、諸事情*1で.NET Framework 2.0系である必要がある場合には使えません。 具体的にはLINQとかHashSetとかですね。
幸いなことに、F# を使えば.NET Framework 2.0をターゲットにしつつLINQにあるような関数やSetが使えます。 というわけで移植しました。
https://github.com/pocketberserker/Diff.Match.Patch
immutableに改変するのも大変つらそうだったのでmutableのままです。 どうしてこうなった…的なコードもたくさん出てきます。 実装に関してはそっ閉じすべき事案でしょう。
使い方は大体C#版と同じように使えるのではないでしょうか。
*1:実際に使いたかったPersimmon.Assertionでは.NET 2.0を最低ラインにしている
java-object-diff を F# に移植
久しぶりの移植芸です。
移植理由
Persimmonの開発に必要みたいな話になり、かつ移植する時間のありそうな人間が他にいなかったとかそういう。
先達
https://github.com/SQiShER/java-object-diff
さすがJavaさん、探せばだいたいでてくる。
成果物
https://github.com/pocketberserker/FSharp.Object.Diff
3月中旬に始めたので、まともに動かせるようになるまで二か月かかった計算に。 まぁ、FSDNやら業務のあれそれやら遊びといったことを考えると妥当な数値っぽい感じがしますね?
あれそれ
移植元はJavaなので、Javaを前提に実装されているわけですね。 なので、当然仕様の差が出てきます。
- Javaにプロパティはないが、.NETにはある
- フィールドのdiff取得機能はomit
- F#では必要ないと思うんだ
このあたりは仕様の話。
ここから実装の時に遭遇した(または現在進行形)の問題。
- Javaの一部のMapだとkeyにnullが突っ込める
- .NETのDictionaryは突っ込めないんだよねーとなってちょっと悩んだ
- Map.getでnullが返ってくる
- そういえばそんなんだったなーとか
- null nullしている
- Javaだもんね、仕方ないね
- 移植を優先した関係で F# 版もnull nullしてます…
- Some nullさん…
- F#のレコードがほぼ使えない
- 移植元がmutable前提なのでしょうがない
- abstract classが出てくるとレコード使えないしね
- .NETのコレクションの基底となるインターフェースは?
- .NETの連想配列の基底となるインターフェースは?
- 比較演算子とか
- Javaの
==
はObject.ReferenceEqual
に、equals
は=
に読み替えるの、わりと難しい - これをミスして何度か嵌った
- Javaの
- equality制約を回避したい
- F#だと
=
を使うと型パラメータがequality
制約を要求する - が、diffライブラリなのでそんな制約は外したい
- ので、比較する部分で
box x
を挟んでobj
で比較するバッドノウハウを駆使して制約を回避している
- F#だと
まとめ
nullよりもコレクションの仕様の差やtype eraseかそうでないかなど、もっと別な場所で精神が削れました。
Microsoft MVP for Visual Studio and Development Technologies(F#)を受賞しました
気がついたらまたカテゴリが変わっていましたが、今年で4回目の受賞となります。 Visual Studio and Development Technologiesとカテゴリ名が長すぎて未だに覚えられません。 覚えられないのでF#と言い張っておきます。
仕事でF#を使わなくなって久しいですが、これからも何かしら発信していけたらと思います。
特にXamarinとかUnityとか、F#の情報が少ない部分に手を入れられたらいいなとは思っていますが、こればかりは調べる体力があるか(そして気が向くかどうか)次第なのでなんとも……。
え、仕事で使わないのかって? 禁則事項です。
というわけで、今年度もよろしくお願いします。
継続を利用してAsyncコンピュテーション式を実装できるか試す
Combine Deep Dives - ぐるぐる~ という記事にこういう文章があります。
このあたりを解決するために、Stateを使ったり継続を使ったりできるかもしれませんが、Async では未検証です。
そういえば検証したことはなかったなと思ったので、試してみました。
コード
コンパイルは通るしテストも通る、という代物は下記コードです。 ただし、テストは通ったけれど本当に非同期に動いているかどうかまでは検証していないというレベルです。
Option、Listの実装と異なる部分
OptionやListでの実装は、パターンマッチによって値を分解することで包まれていた値を取り出すことができるのでわりと簡単に実装できていました。
しかしAsync
はパターンマッチで分解できません。
というわけで、とりあえず各メソッドでは関数をAsync
で返すようにします。
そしてRun
メソッドでは値を返したいのでasync.Return
を渡します。
ゼロ値の処遇
今回実装したAsyncBuilder
ではZero
メソッドを提供せず、AsyncWithZeroBuilder
ゼロ値を渡すようにしました。
が、既存のコンピュテーション式よりもAsyncBuilder
の表現力が落ちるためちょっと納得いってません。
本当は「Return
やReturnFrom
が呼び出されなければコンパイルエラー」というビルダークラスを作りたかったのですが、if then ce
というコンピュテーション式の変形においてce
とZero
の型が一致しなければならず、そこを通過しつつRun
メソッドのみコンパイルエラーになるような実装をついに導き出せませんでした…無念。
未検証の作戦として「Run
で返す値をAsync<'T -> Async<'U>>
のままにする」というものがあります。しかし、これはこれでAsync.RunSynchronous
やAsync.Start
などの多くのメソッドをラップする必要があるので悩みどころです。
まとめ
コンピュテーション式について考えると休日が吹き飛ぶの、つらい。