RushCheckにモナドをみた
coarbitaryって何やってるんだろうということを理解するためにinteger.rbのInteger#coarbitaryを見てみました。
無断引用です。まずかったら教えてください。
# this method is needed to use Coarbitrary. def coarbitrary(g) m = (self >= 0) ? 2 * self : (-2) * self + 1 g.variant(m) end
どうも、自分自身の2倍くらいの数をvariantに渡しているようです。じゃあ、variantはどうなっているのか見てみました。
gen.rbにあります。gはGenクラスかそのサブクラスのインスタンスが入るようです。
# Gen provides functions for generating test instances. class Gen いろいろ省略 # to initialize Gen object, it requires a block which takes two # variables. The first argument of block is assumed as an integer, # and the second one is assumed as a random generator of RandomGen. def initialize(&f) @proc = f end # value is a method to get the value of the internal procedure. # value takes two variables where the first argument is assumed as # an integer and the second one is assumed as a random generator of # RandomGen. def value(n, g) @proc.call(n, g) end # variant constructs a generator which transforms the random number # seed. variant takes one variable which should be an # Integer. variant is needed to generate random functions. def variant(v) self.class.new do |n, r| gen = r v.times { gen, dummy = gen.split } value(n, gen) end end
はじめてみたとき何をやっているか全然わからなかったです。
色々考えたり、調べたりして次のようなことが分かってきました。
- Genクラスはnewするときに引数にブロックをとる。そのブロックはProcオブジェクトに変換されてinitalizeで@procに格納される。
- newの引数で渡すブロックは引数を2つとる。2つめの引数は乱数を生成するための手続き、1つめの引数は2つ目の手続きに渡す引数でおそらく乱数の種
- newの引数を渡すブロックは次の2つの値を返す。
- 生成した乱数
- 新しく乱数を生成するための手続き
3に気が付いて、これはモナドじゃないかなと思いました。RushCheckの生い立ちを考えると可能性は高いです。モナドはマスターしようと何度もトライして挫折しています。Rubyで実現できるとなればもうちょっと理解が進むんじゃないかなと思います。
よっしゃー、これでRushCheckは理解したと思ったのですが、ところでsplitって何やってるんだろうと疑問に思ってrandom.rbを読んでみたら・・・、先はまだ長そうです。