scalacheckの内部構造をいじって遊んでみた
SPECIAL DAY Scala Hack-a-son 143th in KABUKIZA - Scala Meetup Group in Tokyo | Doorkeeper
たまにはハッカソンもいいかなと思って参加してみました(噂の rpscala に参加してみたかったというのもある)。
で、当日は scalacheck をいじっていたコードがようやくテストに通るようになったので書き残しておきます。
注意事項
この記事のコードは pull request を送るつもりはないので、そのことを認識して読んでください。
今のところ scalacheck を fork して別プロジェクトとして育てるつもりもないです。
やりたかったこと
基本方針は "いらないものを消す" です。
- scalajs の クロスコンパイル設定を有効にする
- Gen で
Option[T]
ではなくT
を返すようにする - 内部表現
R
trait を削除する - scalaz で置き換えられそうなコードを置き換える
- ランダムを rng か spire/random に差し替える
現時点でできたのは R
を消すまでです。
クロスコンパイル設定
https://github.com/pocketberserker/scalacheck/commit/c7c47204c7245d1a9830aca79b8550b9e63052fc
2015/02/12 時点の scalacheck の master ブランチは Windows 上ではそのままだとビルドできません。 クロスコンパイルのためのコード共有を、Windows で認識できないシンボリックリンクで行っているためです*1。
対処法は sbt-scalajs の要求にあわせてディレクトリ構成変えたり設定変更した程度です。
これに関してはハッカソンの数日前に作業してました。
Gen で T を返す & R trait を削除する
https://github.com/pocketberserker/scalacheck/commit/6d03207ceee9caf6a9e7ad2cb518c532372bd826
quickcheck 系でデータの生成に失敗することはそうそうないので、 Option を返さないようにしました。
…が、ここで suchThat
メソッドの実装でスタックオーバーフローしたり無限ループに陥ったりして、全然進まず。
結局当日の進捗は駄目でした。
後日、よく考えたら一部実装の変更に伴い suchThat
を呼び出すべきではない部分を発見したので、これを削除したら動くようになりました。
R
trait は Gen 用のラベルや sieve を持っているのですが、これもいらないだろうということで削除。
リファクタリングみたいなこと
https://github.com/pocketberserker/scalacheck/commit/e923d97a2ddfeb9b158bcb9524ce4ca844b7eb32
scala.util.Random から別のものに差し替えたいな、と思って依存関係だけ設定したのですが、作業できずじまい…。
Arbitrary 削除断念
CoArbitrary を自前実装してしまったので、消すのも変だなと思い残してます。
その他
値が生成できるまでがんばってしまうので、多少実行速度が劣化したような感覚… filter
や suchThat
のご利用は計画的に。
まとめ
rpscala は懇親会も含め楽しかったです。
scalacheckのcommandsパッケージをPersimmon.Driedに移植
ちったーを眺めていたら
というつぶやきが見え、そういえば干し柿にはまだ移植してなかったなーと気が付いたので、休日を使って実装してみました。
参考資料
http://scalacheck.org/files/scaladays2014/#1
これ読めばわかるはず。
干し柿での実装
他に方法が思いつかなかったので、クラスベースの実装になっています。
あと、Scalaの
trait Command { ... type Result ...
のような芸当はできないので、型パラメータで代用してます。
で、これと StructuredFormatDisplay
属性がオブジェクト式に対して働いてくれない(?)影響で BoxedCommad
という残念な型が一つできたわけです…。
実行速度は今のところ遅いです。 一部さぼっているところ*1を修正したら少しはマシになるかもしれませんが、体感はそんなにかわないのではないでしょうか。
Redis のサンプルはそのうち移植したいところですね。
おわりに
機能は充実してきた干し柿さん、いったいどこへ向かうやら…まぁ、その前に Arbitrary インスタンスを増やさないといけないのですががが。
あと Persimmon 本体の進捗はありません、すみません…orz
*1:主にList.revして~してList.revする、というさぼり
CoArbitraryの実装比較
全国 QuickCheck 系ユーザの皆様こんにちは、もみあげです。
今日は
CoArbitraryとScalacheck - scalaとか・・・
に関連して、各言語の QuickCheck 実装の CoArbitrary
や Arbitrary (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 を変更するものです。
詳しく知りたい場合はソースコードを読みましょう。
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
に包まれた Gen
を Monad
な値を生成する 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/Gen.java#L637
promote
は Monad ではなく、1引数関数クラス F
が生成される Gen
を返します。
purescript-quickcheck
https://github.com/purescript/purescript-quickcheck/blob/v0.4.0/src/Test/QuickCheck.purs#L25-L26
promote
が repeatable
という名称なのと、 Monad
ではなく関数が対象です。
idris-quickcheck
Arbitrary
がほしいなら双対も一緒に定義しろ、という潔さです。
SwiftCheck
promote
は Monad
でも関数でもなく Rose
が対象です。
Persimmon.Dried
今日*1実装しました。
https://github.com/persimmon-projects/Persimmon.Dried/pull/2
たぶん動いています。
Gen.Apply
が 'T option
を返すので、こいつをなんとか 'T
にする苦肉の策として こんな感じにごまかしてます。
FsCheck
考え方はかわらないけど、実装が根本から異なるので気にしない方向で。
まとめ
CoArbitrary
の考え方は共通している- Idris のみ
Arbitrary
でcoarbitrary
の実装を要求する variant
関数はシグネチャは同じだが実装はばらばら(乱数生成器に依存する?)promote
はMonad
か関数か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 はリリースせずお蔵入りしようかとも考えましたが
@pocketberserker もしかしたらパフォーマンス比較などをしたくなるかもしれない?ので、(一旦現状でリリースしてその後ある程度放置でもいいので)リリースしてみてもいい気はします
2015-01-06 10:17:58 via Twitter Web Client to @pocketberserker
これは確かにその通りだと思ったのと、
- msgpack4z は Scala 2.11 のみ、scodec-msgpack は 2.10 もサポート
- scodec-core が次バージョンから scalaz 非依存にするかも? https://github.com/scodec/scodec/pull/39
という状況もあるので、一旦このタイミングでリリースしておくことにしました。
リリースのための準備(アカウント作成等)
で、リリースのために sonatype アカウントを作成してビルド設定をいじりました。
- 自分が作ったScalaライブラリをMaven Centralリポジトリに登録する方法... | PAB@やっぱり求職中なんだよなあ…
- sbt-sonatypeとsbt-release pluginを組み合わせて使うサンプル - scalaとか・・・
この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
インスタンスをコンパイル時に補完します。
なので、別に Arbitrary
が Gen
しかもっていなくても不都合が起きません。
*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
気になったので F# Specification 3.0 を検索したら 8.13.2 にのみ "do bindings" という記述が見つかった。
"let bindings"も同じセクションでしか見つからなかったので、仕様書では bindings という単語をつかわない模様。
モジュール内での do
に関しては 10.2.5 で "do statements" と記載されている。