CoArbitraryの実装比較

全国 QuickCheck 系ユーザの皆様こんにちは、もみあげです。

今日は

CoArbitraryとScalacheck - scalaとか・・・

に関連して、各言語の QuickCheck 実装の CoArbitraryArbitrary (a -> b) インスタンスについてみていきましょう。

CoArbitrary とは

QucikCheck のドキュメントによると、関数値の生成に使われる型クラスらしいです。

-- Haskell での定義
class CoArbitrary a where
  coarbitrary :: a -> Gen b -> Gen b

値と Gen から新たな Gen を生成できます。 第2引数の Gen が必要なのは、最終的な値を生成するための Gen を作るために、乱数生成器が必要だからだと思ってます。

CoArbitraryインスタンスを作るときには、 variant という関数を用いることが多いです。

-- Haskell での定義
Integral n => n -> Gen a -> Gen a

この関数は Gen の seed を変更するものです。

詳しく知りたい場合はソースコードを読みましょう。

Test/QuickCheck/Arbitrary.hs

Arbitrary (a -> b)

では、僕らの欲したインスタンスを見てみましょう。

-- Haskell での定義
instance (CoArbitrary a, Arbitrary b) => Arbitrary (a -> b) where
  arbitrary = promote (`coarbitrary` arbitrary)

promote 関数という関数は

http://hackage.haskell.org/package/QuickCheck-2.7.6/docs/Test-QuickCheck-Gen-Unsafe.html

Monad に包まれた GenMonad な値を生成する Gen にあげる関数です。 lift と思ってもよさそうに見えますね。

promote に適用する関数値の型は a -> Gen b であり、 関数も Monad なので条件を満たしています。

流れをまとめると

  • 適用された値をから戻り値を生成するジェネレータの seed を変更する関数を作る
  • promote で lifting させる
  • あたかも関数を生成したようにみせかけて property に渡す
  • 関数適用したら Gen から戻り値がランダムに生成される

といったところでしょうか。

なお、一連の情報からこのインスタンスから得られる関数値の適用結果を使って何かテストするのは避けるべきということが伝わってくる気がします。

実装比較

てきとーに解説を済ませたところで、実装をみていきましょう。

Functional Java

https://github.com/functionaljava/functionaljava/blob/v4.2/core/src/main/java/fj/test/Coarbitrary.java

https://github.com/functionaljava/functionaljava/blob/v4.2/core/src/main/java/fj/test/Gen.java#L637

promoteMonad ではなく、1引数関数クラス F が生成される Gen を返します。

purescript-quickcheck

https://github.com/purescript/purescript-quickcheck/blob/v0.4.0/src/Test/QuickCheck.purs#L25-L26

https://github.com/functionaljava/functionaljava/blob/v4.2/core/src/main/java/fj/test/Variant.java

https://github.com/purescript/purescript-quickcheck/blob/d464c5c31313ea6737ac46a8335596eeefe74550/src/Test/QuickCheck/Gen.purs#L49

promoterepeatable という名称なのと、 Monad ではなく関数が対象です。

idris-quickcheck

https://github.com/david-christiansen/idris-quickcheck/blob/51b7f619a3203c20ef8d8e30007bd83dba9fd386/QuickCheck.idr#L96

Arbitrary がほしいなら双対も一緒に定義しろ、という潔さです。

SwiftCheck

https://github.com/typelift/SwiftCheck/blob/00d6a7f5482da1671e2dd56f71068d6e33a8bdc8/SwiftCheck/Arbitrary.swift#L365-L367

https://github.com/typelift/SwiftCheck/blob/00d6a7f5482da1671e2dd56f71068d6e33a8bdc8/SwiftCheck/Gen.swift#L115

https://github.com/typelift/SwiftCheck/blob/00d6a7f5482da1671e2dd56f71068d6e33a8bdc8/SwiftCheck/Combinators.swift#L13

promoteMonad でも関数でもなく Rose が対象です。

Persimmon.Dried

今日*1実装しました。

https://github.com/persimmon-projects/Persimmon.Dried/pull/2

たぶん動いています。

Gen.Apply'T option を返すので、こいつをなんとか 'T にする苦肉の策として こんな感じにごまかしてます。

FsCheck

https://github.com/fsharp/FsCheck/blob/3d7f4d47d6cff65ecdbcc31e60f007b54012de9f/src/FsCheck/Arbitrary.fs#L482

考え方はかわらないけど、実装が根本から異なるので気にしない方向で。

まとめ

  • CoArbitrary の考え方は共通している
  • Idris のみ Arbitrarycoarbitrary の実装を要求する
  • variant 関数はシグネチャは同じだが実装はばらばら(乱数生成器に依存する?)
  • promoteMonad か関数か Rose が対象
  • 実装はそんなに難しくない?

だいたい似たような実装になっていますが、Gen の構造が他と異なる Persimmon.Dried が若干強引な印象です。 実際、Persimmon.Dried の移植元である scalacheck でも以下の issue で同様の指摘がなされています。

https://github.com/rickynils/scalacheck/issues/136

scalacheck の動向に注視したいところですね。

それはそれとして、Functional Java やばい(褒め言葉)。

*1:ブログ投稿日 or pull request マージ日参照

sonatypeのアカウントを作成してscodec-msgpackをpublishした

数日前(?)に xuwei さんが ,msgpack4z というライブラリをリリースしたことが(少なくとも私の中では)話題です。

msgpack4zというmsgpackのScala用ライブラリを作った - scalaとか・・・

こちらの開発が続くだろうし、私が作った scodec-msgpack はリリースせずお蔵入りしようかとも考えましたが

これは確かにその通りだと思ったのと、

という状況もあるので、一旦このタイミングでリリースしておくことにしました。

リリースのための準備(アカウント作成等)

で、リリースのために sonatype アカウントを作成してビルド設定をいじりました。

この2つの記事が非常に参考になりましたというか、ほぼそのままです。 ちなみに Windows(Surface Pro 3) で msysgit な環境で作業しました。

確かに、アカウント登録は面倒ですけど、それ以降は楽でしたね。 sbt-sonatype, sbt-release パワーでしょうか。 そういえば主要リポジトリを見ていると最近は sbt-buildinfo プラグインも使うプロジェクトが多いみたいですね。

scodec-msgpack の今後?

xuwei さんの記事の

  • データ型それぞれ作るの面倒
  • 中間データ発生して効率よくなさそう(計測したわけではない)

この記事はわりと同じ感覚です。 これだけの型を作るのは面倒ですし、パターンマッチとかで処理が増えてしまうので…。

とはいえ、純粋関数型的アプローチでどこまでパフォーマンスを得られるかは気になるので、対応できる部分があれば修正していきたいとは思っています。

(scodecが早くなればタダ乗りできますし)


あと記事と全然関係ないけど各種msgpackライブラリで任意回データを投げ合ってvalidかどうか(いつの仕様ならvalidか)一覧にするアプリ作ってみたくなってきた。

Persimmon用のランダムテストライブラリ案(もしくは scalacheck の F# 移植)

あけましておめでとうございます。

さっそくですが進捗どうですか? 私は駄目でした(Persimmon用VS Test Explorer拡張的な意味で)。

今回はランダムテスト実装の話です。

Persimmon と FsCheck の相性について

FsCheck には IRunner という、拡張ポイントがあります。 この型を実装して Config に設定することで、実装した各メソッドがコールバックされます。

が、これは このコメント にもある通り、xUnit.NET や NUnit ぽいものとは相性が良いものの、型を欲する Persimmon とは若干相性が良くないです。

では、 Runner だけ自力実装…というのも考えたのですが、断念しました。 実行に必要そうないくつかのモジュールや型が internal になっており、互換を崩してまであのエグい実装をコピーしてメンテしたいかというと、うーん…。

FsCheck の公開関数が unit を返す関数ばかりなのは何かの呪いですかね…。

そんなこんなで、FsCheck との連携は使いやすいかなんともいえないので、代替案のライブラリを作ってみようと思ったのが昨年末の話です。

干し柿(という名の scalacheck 移植)

というわけで、大晦日あたりから本命の息抜きに作ってみました。

persimmon-projects/Persimmon.Dried · GitHub

この干し柿は scalacheck をベースにしています。 手を入れるうちにいろいろ変わっていくと思うので、現時点では、という注釈つきですが。

FsCheck と scalacheck がどの程度異なるかは下記記事を参考にしてみてください。

FsCheck と ScalaCheck とを見比べる — a wandering wolf

以下、ちょっとした違いについて書いてみます。

Arbitrary

scalacheck の forAll などは型クラスを用いて Arbitrary インスタンスShrink インスタンスコンパイル時に補完します。 なので、別に ArbitraryGen しかもっていなくても不都合が起きません。 *1

しかし、この方法は正攻法で書いた場合の F# とは相性が悪いです。 そのため、 Persimmon.Dried では Arbitrary にシュリンカとプリティプリンタも持たせ、 forAll などには明示的に Arbitrary を適用する方式にしています。

プリティプリンタ

このあたりは scalacheck に似せています。 そういう理由もあって型に対応する出力を自分でカスタマイズ可能です。

型クラス

とはいえ、型クラスを全く使わないわけにもいきません。 なぜかというと、 Prop 型への変換は基本的に単純作業になりやすいからです。

そのため、 Prop を返す関数や演算子では基本的に PropApply という値を第1引数にとるに引数メソッド Instance を持つ第2引数の型に限って、inline 展開と型推論によって型を変換します。

現時点では

  • bool
  • PropResult
  • Prop
  • Persimmon.TestResult
  • Persimmon.AssertionResult

が変換対象です。

乱数生成

FsCheck は自前の、 scalacheck は標準のランダムモジュールを使っています。

Persimmon.Dried ではどうしようかなと迷ったのですが、この際なので FsRandom を使ってみることにしました。 既存かつ精度も申し分ないので問題ないと思っています。

実は単独でも動く

scalacheck ベースなので、当たり前といえば当たり前の話です。 ただ、 ConsoleReporter とコマンドラインパーサを(意図的に)実装していないので、スクリプトで実行しても何も出力されません。

この辺りも実装するかどうかは気分次第ですね。

おわりに

また一つ、オレオレライブラリが増えてしまった。

*1:Arbitrary 型がなくてもよいのでは、といわれることがあるのはこのためだと思っています

2014年簡易振り返り

寝落ちしていたらこの時間になっていたので、簡易版で。

生活

  • 25歳定年説の歳だったけど新卒2年目(だった年)に定年という悲しみのネタになりそうだったので記事はかかなかった
  • 定時に帰ると時間が空く
  • 朝の時間帯にパf-マンスがでない
  • 東京に行く回数がふえてしまった(移動時間もったいない疑惑)
  • 俺タワーというゲームの影響(?)で工具にも興味がでてきた
  • 年末年始に自炊することを…強いられているんだ!
  • 「これは!」とおもったゲームをブクマするだけでなく買うべき

あとは目立った変化なし。 いいのかこれで…

言語など

  • F#: 趣味と仕事あわせて一番書いた
  • Scala: 趣味でいろいろ触った
  • C#: 仕事で
  • PowerShell: 少し書いた(が、まだ全然なので継続)
  • 英語: 8月と年末にちょっと頑張った

来年は Erlang で何かライブラリを書いてみたいところ。 手を出すだけなら PureScript か Rust ?

年明けは、 Persimmon 用の VS TestExplorer 拡張と QuickCheck 系ライブラリ作りあげたい。

そして英語はどうしたものかな…。 それと某コンピュテーション式の本も再開しないと。

イベントとか

  • Functional 忍者で何回かしゃべった
  • Scalaz 勉強会でしゃべった
  • 函数型なんたらの集い 2014 Tokyoを開催した

記事かけてない…関係者の皆様、ありがとうございました。

もうちょっと他のイベントでも発表できればよかったかな。

締め

毎年移動してばかりだ。

スコープやオフサイドルール小話

この記事は F# Advent Calendar 2014 の28日目の記事です。 引き続きガス欠気味なので小ネタで。

module Hoge =

  let hoge =
    // lazy もオフサイドルールの対象である
    lazy
      let a = 1
      a + 1

  do
    // スコープが異なるので定義できる
    let hoge = 1

    let mutable x = 0
    
    x <-
      printfn "test"
      1

    // これは無理
    // x <- printfn "test"
    //   1

  // 変数 x は上記 do 束縛のスコープ内のみ有効なので、以下のコードはコンパイルエラー
  // let fuga = x

ところで、 domsdn さん曰く do束縛らしい。

気になったので F# Specification 3.0 を検索したら 8.13.2 にのみ "do bindings" という記述が見つかった。 "let bindings"も同じセクションでしか見つからなかったので、仕様書では bindings という単語をつかわない模様。 モジュール内での do に関しては 10.2.5 で "do statements" と記載されている。

1ファイル内に複数のnamespaceを書く

この記事は F# Advent Calendar 2014 の28日目の記事です。 ガス欠気味なので超小ネタで。

namespace Hoge

type Fuga() = class end

namespace Foo

type Bar() = class end

このコードは1ファイル内でコンパイルできます。 =が必要ないのがミソですね。

入れ子の名前空間にしたい場合は、完全修飾で

namespace Hoge

type Fuga() = class end

namespace Hoge.Foo

type Bar() = class end

と書けばよいです。

F# 製ユニットテスティングフレームワーク Persimmon について #FsAdvent

F#

この記事は F# Advent Calendar 2014 の27日目の記事です。

内容は今年の10月末あたりから作っている Persimmon というフレームワークについてです。

諸注意

本記事に書くことはあくまでpocketberserker個人の見解です。 他の開発者がどう考えているのかは

ソースコードやドキュメントなど

Persimmon

persimmon-projects/Persimmon · GitHub

Getting Started があるので使い方はそっちを参考にしてください。

あと public に書き込める場所がほしかったので Gitter に部屋を作りました。

persimmon-projects/ja - Gitter

柿は何であって何でないのか

F# の、 F# による、 F# のためのテスティングフレームワークです。

コンピュテーション式を前提にしている時点で他の言語で使うことは想定していません。 そして、少なくとも私は極力複数言語をプロダクションコードに採用したくない(メンテナンス面倒)ので、ほぼ F# オンリーでの開発になるでしょう。*1 そういうわけで上記の見解です。

F# でユニットテストというと NUnit (とその Wrapper の FsUnit)が多数派です。 ビルドツールの FAKE にはこのほかに xUnit.NET、 MSpec のヘルパー関数が用意されています。 FsCheck も結構使われていますが、あれは性質が異なるので今回の話では対象外です。

さて、この3つのテスティングフレームワークには共通点があります。 それは C# で実装されている点と、既存テスティングフレームワークの手法を踏襲して実装されている点です。

前者はともかく後者については、

  • 期待値と実行結果の型が異なっても実行時エラーでしか気づけないことがある
  • 属性ベースのテスト識別

という問題があります。 属性に関しては単に面倒くさいのと、渡せる型に制約があって一部F# の型は使えない点ですかね。

特徴と類似品

特徴はドキュメントに書いてあります。 まぁ、それだけでおわらせるのもアレなのでちょっとだけ追加。

副作用

Persimmon では、変数、unit を引数にとる関数、static member なプロパティがテスト実行の対象になります。 変数束縛で済ませるか関数にするかは、そのテストに副作用が含まれるかどうかで判断するのが良いのではと思っています。 unit をみたら副作用があるかもしれないと思え、ということですね。

型安全

NUnit の Assertion は型安全ではなかったりしますが、Persimmon の Assertion はよほど変なことをしない限りは型安全です。

コンピュテーション式

「属性を使わないなら、型で表現すればいいじゃない」

とはいえ、ただ型で表現するだけだと色々と面倒なこともあるので、Persimmon ではコンピュテーション式で記述できるようになっているとか、いないとか。

現時点で、master ブランチに4つのコンピュテーション式が存在します。

  • テストの記述
  • parameterized test の記述
  • 例外発生を期待する
  • Asyncな値を実行し非同期例外を補足する

テスト、アサーションの合成

Persimmon のテストやアサーションは値を返します。 そして、 unit を返すことは他のテストやアサーションに影響を与えないことを宣言することになります。 逆に、テストやアサーションの返り値を使って別のテストやアサーションを行うということは、それらが別のテスト、アサーションの通過/違反に依存していることを明示します。

テストを合成できることで

  • 状態を変更するテストと状態を変更しないテストの分離
  • テストの重複除去

を意識できるのではないか、と考えています。 あとはテストを解析することでテストの依存グラフを出力できるなるはずなので、何かしら役にたたないかなと。 テストを合成できるようにして依存グラフ云々みたいな話は過去に id:kyon-mm さんとも話していた気がするけど、参照実装や資料があるのかはちょっとわからないです…。

Monad ではない(はず)

何らかの制約をかければあるいは…ですが、結合則を証明できる気がしない。

AssertJ

最近になって存在を知った AssertJ というライブラリがあります。 Persimmon 実装中に kyon-mm さんに存在を教えてもらったのでこのライブラリの影響を受けた、というわけではないのですが、やりたいことの一部が似ています。

"継続可能なアサーション" は AssertJ では "Soft Assert"と呼ばれているようです。 この名前を踏襲するかは未定ですが、少なくとも新しい概念ではないのは確かなので、そのうちドキュメントに反映したいところです。

AssertJ の SoftAssertion との違いは

  • AssertJは任意、Persimmonは強制
  • AssertJの実装はstateful、Persimmonの実装はstateless
  • AssertJのAssertionは void だが、PersimmonのAssertionは値を返すことができる

SoftAssertionsが stateful なのはSoftAssertionのAPIドキュメントに書いてあります。 また、 assertAll メソッドを呼び出さないとテストに通るだろうこともこのドキュメントに書いてあります。

これはこれでありだと思いますが、合成できるなら合成したほうが楽かもね、とも思います。

NUnit, xUnit.NET, MSTest

主な違いは

  • 型安全性
  • 属性ベースと型ベースの違い
  • 例外によるハンドリングか値を返すか

です。

.NETの枠組みで既存の代替物となることはないでしょう。 テストとプロダクションコードが別言語でも良いと思っている人は移行する可能性はありますが、C# Wrapper を提供しない時点で C#er の方々が使う確率は減ります。 F#書きづらい(or 嫌い)という人もいるでしょうし。 あくまで、F#でのテストの代替物の可能性があるといいね、というレベルです。

モデル

1.0.0 αリリース以前にモデルが一度変更されています。 どれくらいかわったかというと これくらい

実装前の検討段階の資料はプロジェクトのWikiにありますが、最新版は存在しません。 というわけで、最新版の日本語情報を作ってみました。 ある程度固まったら英語に書き直してから日本語に再翻訳して公式ドキュメントに反映したいと思います。

エラーが AssetionResult<'T>ではなくTestResult<'T>にあるのは、例外発生時は多くの場合テストの続行自体不可能である場合が多いためです。

Visual Studio Test Explorer 対応

1.0.0 をリリースしたら作業を再開する予定です。 リフレクション使って実装しないといけないらしいので既にやる気が減衰していますが、まぁないと不便だし一回作れば楽になるのはわかっているので…冬休みの宿題ですね。

問題点

現時点で問題かなと思っていることをさらっとかいてみます。

assertion が貧弱

現状の Assertion は等値比較と述語の真偽比較くらいしか関数がないので貧弱です。 とはいえ、assertHoge 関数を増やしても覚えるのが面倒ですし、言葉つなぎ系は面倒なのであまり実装したくないなという話もあって今の状況なのですが…。

ぱっと思いつく解決策の一つは Power Assert ですが、この issue を見てもらうとわかる通りPower Assert ではない別の Assert を模索しようという方針になっています。

実践的なサンプルが少ない

これはそのうちアプリケーション作ってみないとなー、とかそういう話ですね。 今の FizzBuzz とポーカーの例だけでは情報量が少ない…。

一部既存ライブラリとの相性が悪い

NUnit や xUnit.NET と連携することを想定しているライブラリは、その性質上 例外を用いた Assertion の通知を行うものが多いです。 そしてこの方法は、Persimmon との相性はあまりよくありません。

こればかりはどうしようもないので、「ないなら作るしかねぇ」的な方針にせざるを得ないですね。 ぶっちゃけ FsCheck がそんな感じで相性が良いとは言えないので、 Persimmon 用に作ることを画策しています。

開発体制

今のところ3名で開発が行われています。

  • Gab-km さん
    • 原案発案
    • 怒涛のドキュメントレビュー(超重要)
    • 某ライブラリの開発があるため、現状Persimmonのコードを変更することは稀?
  • bleis-tift さん
    • 改良案発案
    • 新モデルの構築者
    • 忙しくて触れないことがあるようだ
  • pocketberserker
    • その他
    • 上記2名が動けない時に動く役

今のところバランスは保てていますが、今後開発速度をあげたいなら人手がほしいところ。

プロジェクト名の由来

かいつまんで書くと

  • 某社内チャットでプロジェクト名を募集
  • 「秋だから柿か栗では?」
  • とりあえず柿と栗の英単語を調べよう
  • Persimmon、よさそう
  • 「秋にちなんだ名前ってことは秋中に動くようにするんだよね?」「えっ?」

なぜ FAKE を使わないのか

趣味の問題です。

おわりに

1月までには TestExplorer 拡張を含めてリリースしたいところ…。

*1:GUIやパフォーマンスを考える場合はその限りではないです