do_notation 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/{README.rdoc → README.md} +10 -11
- data/Rakefile +3 -24
- data/do_notation.gemspec +14 -0
- data/lib/do_notation.rb +33 -6
- data/lib/do_notation/monad.rb +6 -17
- data/lib/do_notation/monads/array.rb +11 -8
- data/lib/do_notation/monads/identity.rb +16 -0
- data/lib/do_notation/monads/maybe.rb +15 -14
- data/lib/do_notation/monads/simulations.rb +27 -25
- data/lib/do_notation/rewriter.rb +26 -44
- data/{test/array.rb → spec/array_spec.rb} +2 -2
- data/spec/do_notation_spec.rb +16 -0
- data/spec/identity_spec.rb +27 -0
- data/{test/maybe.rb → spec/maybe_spec.rb} +13 -13
- data/spec/monad_plus_spec.rb +37 -0
- data/{test/specs.rb → spec/monad_spec.rb} +22 -21
- data/spec/rewriter_spec.rb +68 -0
- data/spec/simulations_spec.rb +65 -0
- data/{test → spec}/spec_helper.rb +0 -0
- metadata +74 -42
- data/Manifest +0 -15
- data/VERSION +0 -1
- data/do-notation.gemspec +0 -67
- data/ruby-do-notation.gemspec +0 -59
- data/test/monad_plus.rb +0 -37
- data/test/simulations.rb +0 -46
data/.gitignore
CHANGED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
data/{README.rdoc → README.md}
RENAMED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
1
|
+
Haskell-style monad do-notation for Ruby
|
2
|
+
========================================
|
2
3
|
|
3
4
|
Example:
|
4
5
|
|
@@ -6,20 +7,20 @@ Example:
|
|
6
7
|
|
7
8
|
class Array
|
8
9
|
include Monad
|
9
|
-
|
10
|
+
|
10
11
|
def self.unit x
|
11
12
|
[x]
|
12
13
|
end
|
13
|
-
|
14
|
+
|
14
15
|
def bind &f
|
15
|
-
map(&f).inject([]
|
16
|
+
map(&f).inject([], &:+)
|
16
17
|
end
|
17
18
|
end
|
18
|
-
|
19
|
+
|
19
20
|
Array.run do
|
20
21
|
x <- ["first", "second"]
|
21
22
|
y <- ["once", "twice"]
|
22
|
-
|
23
|
+
|
23
24
|
unit("#{x} cousin #{y} removed")
|
24
25
|
end
|
25
26
|
|
@@ -32,11 +33,9 @@ The above code returns the array:
|
|
32
33
|
|
33
34
|
For more examples, see the test suite.
|
34
35
|
|
35
|
-
|
36
|
-
|
37
|
-
There's a gem hosted on Github, so follow the setup instructions
|
38
|
-
at http://gems.github.com and then run:
|
36
|
+
Installation
|
37
|
+
============
|
39
38
|
|
40
|
-
|
39
|
+
gem install do_notation
|
41
40
|
|
42
41
|
By Aanand Prasad (aanand.prasad@gmail.com)
|
data/Rakefile
CHANGED
@@ -1,32 +1,11 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
spec_list = FileList['test/*.rb']
|
1
|
+
require 'rspec/core/rake_task'
|
4
2
|
|
5
3
|
task :default => :spec
|
6
4
|
|
7
5
|
desc "Run all specs"
|
8
|
-
|
9
|
-
t.spec_files = spec_list
|
10
|
-
end
|
6
|
+
RSpec::Core::RakeTask.new('spec')
|
11
7
|
|
12
8
|
desc "Run all specs with RCov"
|
13
|
-
|
14
|
-
t.spec_files = spec_list
|
9
|
+
RSpec::Core::RakeTask.new('rcov') do |t|
|
15
10
|
t.rcov = true
|
16
11
|
end
|
17
|
-
|
18
|
-
begin
|
19
|
-
require 'jeweler'
|
20
|
-
Jeweler::Tasks.new do |gemspec|
|
21
|
-
gemspec.name = "do_notation"
|
22
|
-
gemspec.summary = 'Haskell-style monad do-notation for Ruby'
|
23
|
-
gemspec.description = 'Haskell-style monad do-notation for Ruby'
|
24
|
-
gemspec.email = 'aanand.prasad@gmail.com'
|
25
|
-
gemspec.homepage = 'http://github.com/aanand/do_notation'
|
26
|
-
gemspec.authors = ["Aanand Prasad"]
|
27
|
-
gemspec.add_dependency('ParseTree')
|
28
|
-
gemspec.add_dependency('ruby2ruby', '1.1.9')
|
29
|
-
end
|
30
|
-
rescue LoadError
|
31
|
-
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
32
|
-
end
|
data/do_notation.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "do_notation"
|
3
|
+
s.version = "0.3.0"
|
4
|
+
s.summary = 'Haskell-style monad do-notation for Ruby'
|
5
|
+
s.email = 'aanand.prasad@gmail.com'
|
6
|
+
s.homepage = 'https://github.com/aanand/do_notation'
|
7
|
+
s.authors = ["Aanand Prasad"]
|
8
|
+
|
9
|
+
s.files = `git ls-files`.split
|
10
|
+
|
11
|
+
s.add_dependency('sourcify')
|
12
|
+
s.add_development_dependency('rake')
|
13
|
+
s.add_development_dependency('rspec', '~> 2.0')
|
14
|
+
end
|
data/lib/do_notation.rb
CHANGED
@@ -1,12 +1,39 @@
|
|
1
|
-
|
1
|
+
require 'sourcify'
|
2
2
|
|
3
|
-
|
3
|
+
module DoNotation
|
4
|
+
def self.run(klass, &block)
|
5
|
+
eval(ruby_for(klass, block), block.binding).call
|
6
|
+
end
|
4
7
|
|
5
|
-
|
8
|
+
def self.ruby_for(klass, block)
|
9
|
+
"#{klass.name}.instance_eval { #{ruby_for_block(block)} }"
|
10
|
+
end
|
6
11
|
|
7
|
-
|
8
|
-
|
9
|
-
|
12
|
+
def self.ruby_for_block(block)
|
13
|
+
@cached_ruby ||= {}
|
14
|
+
@cached_ruby[block.to_s] ||= Ruby2Ruby.new.process(DoNotation::Rewriter.new.process(block.to_sexp))
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.pp(obj, indent='')
|
18
|
+
return obj.inspect unless obj.is_a? Array
|
19
|
+
return '()' if obj.empty?
|
20
|
+
|
21
|
+
str = '(' + pp(obj.first, indent + ' ')
|
22
|
+
|
23
|
+
if obj.length > 1
|
24
|
+
str << ' '
|
25
|
+
|
26
|
+
next_indent = indent + (' ' * str.length)
|
27
|
+
|
28
|
+
str << obj[1..-1].map{ |o| pp(o, next_indent) }.join("\n#{next_indent}")
|
29
|
+
end
|
30
|
+
|
31
|
+
str << ')'
|
32
|
+
|
33
|
+
str
|
34
|
+
end
|
35
|
+
end
|
10
36
|
|
11
37
|
require 'do_notation/rewriter'
|
12
38
|
require 'do_notation/monad'
|
39
|
+
|
data/lib/do_notation/monad.rb
CHANGED
@@ -1,24 +1,13 @@
|
|
1
1
|
module Monad
|
2
|
-
|
3
|
-
|
4
|
-
eval(ruby_for(block), block).call
|
5
|
-
end
|
6
|
-
|
7
|
-
def ruby_for block
|
8
|
-
@cached_ruby ||= {}
|
9
|
-
@cached_ruby[block.to_s] ||= "#{self.name}.instance_eval { #{Ruby2Ruby.new.process(Rewriter.new.process(block.to_method.to_sexp)[2])} }"
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def bind_const &block
|
14
|
-
bind { |_| block.call() }
|
2
|
+
def run &block
|
3
|
+
DoNotation.run(self, &block)
|
15
4
|
end
|
16
5
|
|
17
|
-
def
|
18
|
-
|
6
|
+
def bind_const value, &block
|
7
|
+
bind(value) { block.call }
|
19
8
|
end
|
20
9
|
|
21
|
-
def
|
22
|
-
|
10
|
+
def compose a, b
|
11
|
+
bind_const(a) { b }
|
23
12
|
end
|
24
13
|
end
|
@@ -1,21 +1,24 @@
|
|
1
|
+
require 'do_notation'
|
1
2
|
require 'do_notation/monad_plus'
|
2
3
|
|
3
4
|
class Array
|
4
|
-
|
5
|
+
extend Monad
|
5
6
|
|
6
7
|
def self.unit x
|
7
8
|
[x]
|
8
9
|
end
|
9
|
-
|
10
|
-
def bind &f
|
11
|
-
map(&f).inject([]
|
10
|
+
|
11
|
+
def self.bind value, &f
|
12
|
+
value.map(&f).inject([], &:+)
|
12
13
|
end
|
13
|
-
|
14
|
+
|
14
15
|
extend MonadPlus
|
15
|
-
|
16
|
+
|
16
17
|
def self.mzero
|
17
18
|
[]
|
18
19
|
end
|
19
|
-
|
20
|
-
|
20
|
+
|
21
|
+
def self.mplus(a, b)
|
22
|
+
a + b
|
23
|
+
end
|
21
24
|
end
|
@@ -1,31 +1,32 @@
|
|
1
|
+
require 'do_notation'
|
1
2
|
require 'do_notation/monad_plus'
|
2
3
|
|
3
|
-
class Maybe
|
4
|
-
|
5
|
-
|
4
|
+
class Maybe
|
5
|
+
extend Monad
|
6
|
+
|
6
7
|
def self.unit value
|
7
|
-
|
8
|
+
value
|
8
9
|
end
|
9
|
-
|
10
|
-
def bind &f
|
10
|
+
|
11
|
+
def self.bind value, &f
|
11
12
|
if value.nil?
|
12
|
-
|
13
|
+
nil
|
13
14
|
else
|
14
15
|
f.call(value)
|
15
16
|
end
|
16
17
|
end
|
17
|
-
|
18
|
+
|
18
19
|
extend MonadPlus
|
19
|
-
|
20
|
+
|
20
21
|
def self.mzero
|
21
22
|
unit(nil)
|
22
23
|
end
|
23
|
-
|
24
|
-
def mplus
|
25
|
-
if
|
26
|
-
|
24
|
+
|
25
|
+
def self.mplus a, b
|
26
|
+
if b.nil?
|
27
|
+
a
|
27
28
|
else
|
28
|
-
|
29
|
+
b
|
29
30
|
end
|
30
31
|
end
|
31
32
|
end
|
@@ -2,37 +2,39 @@
|
|
2
2
|
# http://eigenclass.org/hiki/warm-fuzzy-things-for-random-simulations
|
3
3
|
# http://eigenclass.org/hiki.rb?c=plugin;plugin=attach_download;p=warm-fuzzy-things-for-random-simulations;file_name=fuzzy-warm-simulations.rb
|
4
4
|
|
5
|
-
|
5
|
+
require 'do_notation'
|
6
|
+
|
7
|
+
module PRNG
|
6
8
|
def next_state(s); (69069 * s + 5) % (2**32) end
|
7
9
|
end
|
8
10
|
|
9
11
|
class Simulation
|
10
12
|
extend PRNG
|
11
|
-
|
12
|
-
|
13
|
+
extend Monad
|
14
|
+
|
13
15
|
attr_reader :f
|
14
16
|
|
15
17
|
def initialize(&b)
|
16
18
|
@f = b
|
17
19
|
end
|
18
|
-
|
20
|
+
|
19
21
|
def self.unit(x)
|
20
22
|
new { |s| [x, s] }
|
21
23
|
end
|
22
|
-
|
24
|
+
|
23
25
|
def self.rand(n)
|
24
26
|
self.new do |s|
|
25
27
|
[s.abs % n, next_state(s)]
|
26
28
|
end
|
27
29
|
end
|
28
30
|
|
29
|
-
def bind(&b)
|
30
|
-
self.
|
31
|
-
x, s =
|
31
|
+
def self.bind(simulation, &b)
|
32
|
+
self.new do |s|
|
33
|
+
x, s = simulation.f.call(s)
|
32
34
|
b.call(x).f.call(s)
|
33
35
|
end
|
34
36
|
end
|
35
|
-
|
37
|
+
|
36
38
|
def play(s = 12345)
|
37
39
|
@f.call(s).first
|
38
40
|
end
|
@@ -40,33 +42,33 @@ end
|
|
40
42
|
|
41
43
|
class Distribution
|
42
44
|
extend PRNG
|
43
|
-
|
44
|
-
|
45
|
+
extend Monad
|
46
|
+
|
45
47
|
attr_reader :a
|
46
48
|
|
47
49
|
def initialize(a)
|
48
50
|
@a = a
|
49
51
|
end
|
50
|
-
|
52
|
+
|
51
53
|
def self.wrap(a)
|
52
54
|
new(a)
|
53
55
|
end
|
54
|
-
|
56
|
+
|
55
57
|
def self.unit(x)
|
56
58
|
wrap [[x, 1.0]]
|
57
59
|
end
|
58
60
|
|
59
|
-
def self.rand(n)
|
61
|
+
def self.rand(n)
|
60
62
|
p = 1.0 / n
|
61
63
|
new((0...n).map{|i| [i, p]})
|
62
64
|
end
|
63
65
|
|
64
|
-
def bind(&b)
|
65
|
-
if
|
66
|
-
|
66
|
+
def self.bind(distribution, &b)
|
67
|
+
if distribution.a.empty?
|
68
|
+
wrap([])
|
67
69
|
else
|
68
|
-
x, p =
|
69
|
-
|
70
|
+
x, p = distribution.a[0]
|
71
|
+
wrap(mulp(p, b.call(x)) + bind(self.new(distribution.a[1..-1]), &b).a)
|
70
72
|
end
|
71
73
|
end
|
72
74
|
|
@@ -77,21 +79,21 @@ class Distribution
|
|
77
79
|
end
|
78
80
|
|
79
81
|
private
|
80
|
-
def mulp(p, l)
|
82
|
+
def self.mulp(p, l)
|
81
83
|
l.a.map{|x, p1| [x, p * p1]}
|
82
84
|
end
|
83
85
|
end
|
84
86
|
|
85
87
|
class Expectation
|
86
88
|
extend PRNG
|
87
|
-
|
88
|
-
|
89
|
+
extend Monad
|
90
|
+
|
89
91
|
attr_reader :f
|
90
92
|
|
91
93
|
def initialize(&b)
|
92
94
|
@f = b
|
93
95
|
end
|
94
|
-
|
96
|
+
|
95
97
|
def self.wrap(&proc)
|
96
98
|
new(&proc)
|
97
99
|
end
|
@@ -107,8 +109,8 @@ class Expectation
|
|
107
109
|
end
|
108
110
|
end
|
109
111
|
|
110
|
-
def bind(&b)
|
111
|
-
|
112
|
+
def self.bind(expectation, &b)
|
113
|
+
wrap{|k| expectation.f.call(lambda{|x| b.call(x).f.call(k)}) }
|
112
114
|
end
|
113
115
|
|
114
116
|
def play(x)
|
data/lib/do_notation/rewriter.rb
CHANGED
@@ -1,63 +1,45 @@
|
|
1
|
-
class Rewriter
|
2
|
-
def
|
3
|
-
|
4
|
-
|
5
|
-
exp.
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
class DoNotation::Rewriter
|
2
|
+
def process exp
|
3
|
+
raise ArgumentError, "expected :iter, got #{exp[0].inspect}" unless exp[0] == :iter
|
4
|
+
raise ArgumentError, "expected s(:call, nil, :proc, s(:arglist)), got #{exp[1].inspect}" unless exp[1] == s(:call, nil, :proc, s(:arglist))
|
5
|
+
raise ArgumentError, "expected nil, got #{exp[2].inspect}" unless exp[2] == nil
|
6
|
+
|
7
|
+
if exp[3].is_a?(Sexp) and exp[3][0] == :block
|
8
|
+
iter, call, nil_val, block = exp.shift, exp.shift, exp.shift, exp.shift
|
9
|
+
raise ArgumentError, "unexpected extra syntax: #{exp.inspect}" unless exp.empty?
|
10
|
+
s(iter, call, nil_val, *rewrite_assignments(block[1..-1]))
|
11
11
|
else
|
12
|
-
|
12
|
+
exp
|
13
13
|
end
|
14
|
-
|
15
|
-
s(:iter, s(:fcall, :proc), nil, *rewrite_assignments(body))
|
16
14
|
end
|
17
15
|
|
18
16
|
def rewrite_assignments exp
|
19
17
|
return [] if exp.empty?
|
20
|
-
|
18
|
+
|
21
19
|
head = exp.shift
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
|
21
|
+
|
22
|
+
if head[0] == :call and head[1] and head[1][0] == :call and head[2] == :< and head[3][0] == :arglist and head[3][1][2] == :-@
|
23
|
+
var_name = head[1][2]
|
25
24
|
expression = head[3][1][1]
|
26
|
-
|
25
|
+
|
27
26
|
body = rewrite_assignments(exp)
|
28
|
-
|
27
|
+
|
29
28
|
if body.first.is_a? Symbol
|
30
29
|
body = [s(*body)]
|
31
30
|
end
|
32
|
-
|
31
|
+
|
33
32
|
[s(:iter,
|
34
|
-
s(:call,
|
35
|
-
s(:
|
33
|
+
s(:call, nil, :bind, s(:arglist, expression)),
|
34
|
+
s(:lasgn, var_name),
|
36
35
|
*body)]
|
37
36
|
elsif exp.empty?
|
38
|
-
[
|
37
|
+
[head]
|
39
38
|
else
|
40
|
-
[s(:iter,
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
def self.pp(obj, indent='')
|
46
|
-
return obj.inspect unless obj.is_a? Array
|
47
|
-
return '()' if obj.empty?
|
48
|
-
|
49
|
-
str = '(' + pp(obj.first, indent + ' ')
|
50
|
-
|
51
|
-
if obj.length > 1
|
52
|
-
str << ' '
|
53
|
-
|
54
|
-
next_indent = indent + (' ' * str.length)
|
55
|
-
|
56
|
-
str << obj[1..-1].map{ |o| pp(o, next_indent) }.join("\n#{next_indent}")
|
39
|
+
[s(:iter,
|
40
|
+
s(:call, nil, :bind_const, s(:arglist, head)),
|
41
|
+
nil,
|
42
|
+
*rewrite_assignments(exp))]
|
57
43
|
end
|
58
|
-
|
59
|
-
str << ')'
|
60
|
-
|
61
|
-
str
|
62
44
|
end
|
63
45
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "DoNotation.pp" do
|
4
|
+
specify "should produce correct output" do
|
5
|
+
array = [:a, [:b],
|
6
|
+
[:c, :d,
|
7
|
+
:e]]
|
8
|
+
|
9
|
+
DoNotation.pp(array).should == <<CODE.strip
|
10
|
+
(:a (:b)
|
11
|
+
(:c :d
|
12
|
+
:e))
|
13
|
+
CODE
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
require 'do_notation/monads/identity'
|
3
|
+
|
4
|
+
describe "Identity:" do
|
5
|
+
specify "just works like sequential code" do
|
6
|
+
value = Identity.run do
|
7
|
+
x <- 1
|
8
|
+
y <- 2
|
9
|
+
|
10
|
+
x + y
|
11
|
+
end
|
12
|
+
|
13
|
+
value.should == 3
|
14
|
+
end
|
15
|
+
|
16
|
+
specify "doesn't do anything special with nil" do
|
17
|
+
lambda {
|
18
|
+
value = Identity.run do
|
19
|
+
x <- 1
|
20
|
+
y <- nil
|
21
|
+
|
22
|
+
x + y
|
23
|
+
end
|
24
|
+
}.should raise_error(TypeError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
@@ -3,24 +3,24 @@ require 'do_notation/monads/maybe'
|
|
3
3
|
|
4
4
|
describe "Maybe:" do
|
5
5
|
specify "one or more nils results in nil" do
|
6
|
-
|
7
|
-
x <-
|
8
|
-
y <-
|
9
|
-
|
10
|
-
|
6
|
+
value = Maybe.run do
|
7
|
+
x <- 1
|
8
|
+
y <- nil
|
9
|
+
|
10
|
+
x + y
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
value.should == nil
|
14
14
|
end
|
15
15
|
|
16
16
|
specify "all non-nil results in complete calculation" do
|
17
|
-
|
18
|
-
x <-
|
19
|
-
y <-
|
20
|
-
|
21
|
-
|
17
|
+
value = Maybe.run do
|
18
|
+
x <- 1
|
19
|
+
y <- 2
|
20
|
+
|
21
|
+
x + y
|
22
22
|
end
|
23
|
-
|
24
|
-
|
23
|
+
|
24
|
+
value.should == 3
|
25
25
|
end
|
26
26
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
require 'do_notation/monads/array'
|
3
|
+
require 'do_notation/monads/maybe'
|
4
|
+
|
5
|
+
describe "MonadPlus:" do
|
6
|
+
[Maybe, Array].each do |monad|
|
7
|
+
describe monad do
|
8
|
+
specify "mzero >>= f = mzero" do
|
9
|
+
monad.bind(monad.mzero){ |x| unit(x+1) }.should == monad.mzero
|
10
|
+
end
|
11
|
+
|
12
|
+
specify "v >> mzero = mzero" do
|
13
|
+
monad.compose(monad.unit(1), monad.mzero).should == monad.mzero
|
14
|
+
end
|
15
|
+
|
16
|
+
specify "mzero `mplus` m = m" do
|
17
|
+
monad.mplus(monad.mzero, monad.unit(1)).should == monad.unit(1)
|
18
|
+
end
|
19
|
+
|
20
|
+
specify "m `mplus` mzero = m" do
|
21
|
+
monad.mplus(monad.unit(1), monad.mzero).should == monad.unit(1)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
specify "guard() prunes the execution tree" do
|
27
|
+
array = Array.run do
|
28
|
+
x <- [0,1,2,3]
|
29
|
+
y <- [0,1,2,3]
|
30
|
+
guard(x + y == 3)
|
31
|
+
|
32
|
+
unit([x,y])
|
33
|
+
end
|
34
|
+
|
35
|
+
array.should == [[0, 3], [1, 2], [2, 1], [3, 0]]
|
36
|
+
end
|
37
|
+
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
2
3
|
require 'do_notation/monads/array'
|
4
|
+
require 'do_notation/monads/maybe'
|
3
5
|
|
4
6
|
describe "Monad.run" do
|
5
7
|
specify "should have lexical scope" do
|
@@ -10,7 +12,7 @@ describe "Monad.run" do
|
|
10
12
|
x <- ["first", "second"]
|
11
13
|
y <- ["once", "twice"]
|
12
14
|
|
13
|
-
|
15
|
+
["#{x} #{foo} #{y} #{bar}"]
|
14
16
|
end
|
15
17
|
|
16
18
|
array.should == ["first cousin once removed",
|
@@ -18,26 +20,38 @@ describe "Monad.run" do
|
|
18
20
|
"second cousin once removed",
|
19
21
|
"second cousin twice removed"]
|
20
22
|
end
|
21
|
-
|
23
|
+
|
24
|
+
it "can be run on the same block multiple times, using different monads" do
|
25
|
+
block = proc do
|
26
|
+
x <- unit(1)
|
27
|
+
y <- unit(2)
|
28
|
+
unit(x+y)
|
29
|
+
end
|
30
|
+
|
31
|
+
[Identity, Maybe, Array].each do |monad|
|
32
|
+
monad.run(&block).should == monad.unit(3)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
22
36
|
specify "should be nestable" do
|
23
37
|
array = Array.run do
|
24
38
|
x <- run do
|
25
39
|
a <- ['A','a']
|
26
40
|
b <- ['B','b']
|
27
41
|
|
28
|
-
|
42
|
+
[a+b]
|
29
43
|
end
|
30
44
|
|
31
45
|
y <- run do
|
32
46
|
c <- ['C','c']
|
33
47
|
d <- ['D','d']
|
34
48
|
|
35
|
-
|
49
|
+
[c+d]
|
36
50
|
end
|
37
51
|
|
38
|
-
|
52
|
+
[x+y]
|
39
53
|
end
|
40
|
-
|
54
|
+
|
41
55
|
array.should == ["ABCD", "ABCd", "ABcD", "ABcd",
|
42
56
|
"AbCD", "AbCd", "AbcD", "Abcd",
|
43
57
|
"aBCD", "aBCd", "aBcD", "aBcd",
|
@@ -45,22 +59,9 @@ describe "Monad.run" do
|
|
45
59
|
end
|
46
60
|
end
|
47
61
|
|
48
|
-
describe '
|
62
|
+
describe 'compose' do
|
49
63
|
specify "should compose two values, discarding the first" do
|
50
|
-
([1]
|
64
|
+
Array.compose([1], [2]).should == [2]
|
51
65
|
end
|
52
66
|
end
|
53
67
|
|
54
|
-
describe "Rewriter.pp" do
|
55
|
-
specify "should produce correct output" do
|
56
|
-
array = [:a, [:b],
|
57
|
-
[:c, :d,
|
58
|
-
:e]]
|
59
|
-
|
60
|
-
Rewriter.pp(array).should == <<CODE.strip
|
61
|
-
(:a (:b)
|
62
|
-
(:c :d
|
63
|
-
:e))
|
64
|
-
CODE
|
65
|
-
end
|
66
|
-
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "Rewriter" do
|
4
|
+
it "leaves uninteresting blocks alone" do
|
5
|
+
in_block = proc { x + y }
|
6
|
+
out_block = proc { x + y }
|
7
|
+
|
8
|
+
process(in_block.to_sexp).should == out_block.to_sexp
|
9
|
+
end
|
10
|
+
|
11
|
+
it "rewrites the <- operator" do
|
12
|
+
in_block = proc do
|
13
|
+
x <- 1
|
14
|
+
y
|
15
|
+
end
|
16
|
+
|
17
|
+
out_block = proc do
|
18
|
+
bind 1 do |x|
|
19
|
+
y
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
process(in_block.to_sexp).should == out_block.to_sexp
|
24
|
+
end
|
25
|
+
|
26
|
+
it "rewrites multiple <- operators" do
|
27
|
+
in_block = proc do
|
28
|
+
x <- 1
|
29
|
+
y <- 2
|
30
|
+
z
|
31
|
+
end
|
32
|
+
|
33
|
+
out_block = proc do
|
34
|
+
bind 1 do |x|
|
35
|
+
bind 2 do |y|
|
36
|
+
z
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
process(in_block.to_sexp).should == out_block.to_sexp
|
42
|
+
end
|
43
|
+
|
44
|
+
it "rewrites statements without a <- to use bind_const" do
|
45
|
+
in_block = proc do
|
46
|
+
x <- 1
|
47
|
+
do_something
|
48
|
+
y <- 2
|
49
|
+
z
|
50
|
+
end
|
51
|
+
|
52
|
+
out_block = proc do
|
53
|
+
bind 1 do |x|
|
54
|
+
bind_const(do_something) do
|
55
|
+
bind 2 do |y|
|
56
|
+
z
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
process(in_block.to_sexp).should == out_block.to_sexp
|
63
|
+
end
|
64
|
+
|
65
|
+
def process(sexp)
|
66
|
+
DoNotation::Rewriter.new.process(sexp)
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
require 'do_notation/monads/simulations'
|
3
|
+
|
4
|
+
EPSILON = 0.001
|
5
|
+
|
6
|
+
RSpec::Matchers.define :approximate_floats do |expected|
|
7
|
+
match do |actual|
|
8
|
+
all_approximate?(actual, expected)
|
9
|
+
end
|
10
|
+
|
11
|
+
def all_approximate?(actual, expected)
|
12
|
+
case actual
|
13
|
+
when Array
|
14
|
+
actual.zip(expected).all? { |pair| all_approximate?(pair.first, pair.last) }
|
15
|
+
when Float
|
16
|
+
(expected - actual).abs < EPSILON
|
17
|
+
else
|
18
|
+
expected == actual
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
roll_3d6 = proc do
|
24
|
+
d1 <- rand(6)
|
25
|
+
d2 <- rand(6)
|
26
|
+
d3 <- rand(6)
|
27
|
+
|
28
|
+
unit(1+d1 + 1+d2 + 1+d3)
|
29
|
+
end
|
30
|
+
|
31
|
+
roll_3d6_b = proc do
|
32
|
+
d1 <- rand(6)
|
33
|
+
d2 <- rand(6)
|
34
|
+
d3 <- rand(6)
|
35
|
+
|
36
|
+
if [d1, d2, d3].include?(5)
|
37
|
+
run do
|
38
|
+
d4 <- rand(6)
|
39
|
+
unit(1+d1 + 1+d2 + 1+d3 + 1+d4)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
unit(1+d1 + 1+d2 + 1+d3)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "Simulation" do
|
47
|
+
specify "generates correct answers" do
|
48
|
+
Simulation.run(&roll_3d6).play.should == 9
|
49
|
+
Simulation.run(&roll_3d6_b).play.should == 9
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "Distribution" do
|
54
|
+
specify "generates correct answers" do
|
55
|
+
Distribution.run(&roll_3d6).play.should approximate_floats([[3, 0.005], [4, 0.014], [5, 0.028], [6, 0.046], [7, 0.069], [8, 0.097], [9, 0.116], [10, 0.125], [11, 0.125], [12, 0.116], [13, 0.097], [14, 0.069], [15, 0.046], [16, 0.028], [17, 0.014], [18, 0.005]])
|
56
|
+
Distribution.run(&roll_3d6_b).play.should approximate_floats([[3, 0.005], [4, 0.014], [5, 0.028], [6, 0.046], [7, 0.069], [8, 0.083], [9, 0.09], [10, 0.09], [11, 0.083], [12, 0.069], [13, 0.063], [14, 0.06], [15, 0.058], [16, 0.056], [17, 0.053], [18, 0.046], [19, 0.035], [20, 0.024], [21, 0.015], [22, 0.008], [23, 0.003], [24, 0.001]])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "Expectation" do
|
61
|
+
specify "generates correct answers" do
|
62
|
+
(1..21).collect { |x| Expectation.run(&roll_3d6).play(x) }.should approximate_floats([0.0, 0.0, 0.005, 0.014, 0.028, 0.046, 0.069, 0.097, 0.116, 0.125, 0.125, 0.116, 0.097, 0.069, 0.046, 0.028, 0.014, 0.005, 0.0, 0.0, 0.0])
|
63
|
+
(1..21).collect { |x| Expectation.run(&roll_3d6_b).play(x) }.should approximate_floats([0.0, 0.0, 0.005, 0.014, 0.028, 0.046, 0.069, 0.083, 0.09, 0.09, 0.083, 0.069, 0.063, 0.06, 0.058, 0.056, 0.053, 0.046, 0.035, 0.024, 0.015])
|
64
|
+
end
|
65
|
+
end
|
File without changes
|
metadata
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: do_notation
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 0.3.0
|
5
11
|
platform: ruby
|
6
12
|
authors:
|
7
13
|
- Aanand Prasad
|
@@ -9,90 +15,116 @@ autorequire:
|
|
9
15
|
bindir: bin
|
10
16
|
cert_chain: []
|
11
17
|
|
12
|
-
date:
|
13
|
-
default_executable:
|
18
|
+
date: 2011-11-14 00:00:00 Z
|
14
19
|
dependencies:
|
15
20
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
21
|
+
name: sourcify
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
17
32
|
type: :runtime
|
18
|
-
|
19
|
-
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rake
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
20
39
|
requirements:
|
21
40
|
- - ">="
|
22
41
|
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
23
45
|
version: "0"
|
24
|
-
|
46
|
+
type: :development
|
47
|
+
version_requirements: *id002
|
25
48
|
- !ruby/object:Gem::Dependency
|
26
|
-
name:
|
27
|
-
|
28
|
-
|
29
|
-
|
49
|
+
name: rspec
|
50
|
+
prerelease: false
|
51
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
52
|
+
none: false
|
30
53
|
requirements:
|
31
|
-
- -
|
54
|
+
- - ~>
|
32
55
|
- !ruby/object:Gem::Version
|
33
|
-
|
34
|
-
|
35
|
-
|
56
|
+
hash: 3
|
57
|
+
segments:
|
58
|
+
- 2
|
59
|
+
- 0
|
60
|
+
version: "2.0"
|
61
|
+
type: :development
|
62
|
+
version_requirements: *id003
|
63
|
+
description:
|
36
64
|
email: aanand.prasad@gmail.com
|
37
65
|
executables: []
|
38
66
|
|
39
67
|
extensions: []
|
40
68
|
|
41
|
-
extra_rdoc_files:
|
42
|
-
|
69
|
+
extra_rdoc_files: []
|
70
|
+
|
43
71
|
files:
|
44
72
|
- .gitignore
|
45
|
-
-
|
46
|
-
-
|
73
|
+
- .rspec
|
74
|
+
- .travis.yml
|
75
|
+
- Gemfile
|
76
|
+
- README.md
|
47
77
|
- Rakefile
|
48
|
-
-
|
49
|
-
- do-notation.gemspec
|
78
|
+
- do_notation.gemspec
|
50
79
|
- lib/do_notation.rb
|
51
80
|
- lib/do_notation/monad.rb
|
52
81
|
- lib/do_notation/monad_plus.rb
|
53
82
|
- lib/do_notation/monads/array.rb
|
83
|
+
- lib/do_notation/monads/identity.rb
|
54
84
|
- lib/do_notation/monads/maybe.rb
|
55
85
|
- lib/do_notation/monads/simulations.rb
|
56
86
|
- lib/do_notation/rewriter.rb
|
57
|
-
-
|
58
|
-
-
|
59
|
-
-
|
60
|
-
-
|
61
|
-
-
|
62
|
-
-
|
63
|
-
-
|
64
|
-
|
65
|
-
|
87
|
+
- spec/array_spec.rb
|
88
|
+
- spec/do_notation_spec.rb
|
89
|
+
- spec/identity_spec.rb
|
90
|
+
- spec/maybe_spec.rb
|
91
|
+
- spec/monad_plus_spec.rb
|
92
|
+
- spec/monad_spec.rb
|
93
|
+
- spec/rewriter_spec.rb
|
94
|
+
- spec/simulations_spec.rb
|
95
|
+
- spec/spec_helper.rb
|
96
|
+
homepage: https://github.com/aanand/do_notation
|
66
97
|
licenses: []
|
67
98
|
|
68
99
|
post_install_message:
|
69
|
-
rdoc_options:
|
70
|
-
|
100
|
+
rdoc_options: []
|
101
|
+
|
71
102
|
require_paths:
|
72
103
|
- lib
|
73
104
|
required_ruby_version: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
74
106
|
requirements:
|
75
107
|
- - ">="
|
76
108
|
- !ruby/object:Gem::Version
|
109
|
+
hash: 3
|
110
|
+
segments:
|
111
|
+
- 0
|
77
112
|
version: "0"
|
78
|
-
version:
|
79
113
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
114
|
+
none: false
|
80
115
|
requirements:
|
81
116
|
- - ">="
|
82
117
|
- !ruby/object:Gem::Version
|
118
|
+
hash: 3
|
119
|
+
segments:
|
120
|
+
- 0
|
83
121
|
version: "0"
|
84
|
-
version:
|
85
122
|
requirements: []
|
86
123
|
|
87
124
|
rubyforge_project:
|
88
|
-
rubygems_version: 1.
|
125
|
+
rubygems_version: 1.8.10
|
89
126
|
signing_key:
|
90
127
|
specification_version: 3
|
91
128
|
summary: Haskell-style monad do-notation for Ruby
|
92
|
-
test_files:
|
93
|
-
|
94
|
-
- test/maybe.rb
|
95
|
-
- test/monad_plus.rb
|
96
|
-
- test/simulations.rb
|
97
|
-
- test/spec_helper.rb
|
98
|
-
- test/specs.rb
|
129
|
+
test_files: []
|
130
|
+
|
data/Manifest
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
lib/do_notation/monad.rb
|
2
|
-
lib/do_notation/monad_plus.rb
|
3
|
-
lib/do_notation/monads/array.rb
|
4
|
-
lib/do_notation/monads/maybe.rb
|
5
|
-
lib/do_notation/monads/simulations.rb
|
6
|
-
lib/do_notation/rewriter.rb
|
7
|
-
lib/do_notation.rb
|
8
|
-
README.rdoc
|
9
|
-
test/array.rb
|
10
|
-
test/maybe.rb
|
11
|
-
test/monad_plus.rb
|
12
|
-
test/simulations.rb
|
13
|
-
test/spec_helper.rb
|
14
|
-
test/specs.rb
|
15
|
-
Manifest
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.2.0
|
data/do-notation.gemspec
DELETED
@@ -1,67 +0,0 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = %q{do-notation}
|
8
|
-
s.version = "0.2.0"
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Aanand Prasad"]
|
12
|
-
s.date = %q{2009-09-29}
|
13
|
-
s.description = %q{Haskell-style monad do-notation for Ruby}
|
14
|
-
s.email = %q{aanand.prasad@gmail.com}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"README.rdoc"
|
17
|
-
]
|
18
|
-
s.files = [
|
19
|
-
".gitignore",
|
20
|
-
"Manifest",
|
21
|
-
"README.rdoc",
|
22
|
-
"Rakefile",
|
23
|
-
"lib/do_notation.rb",
|
24
|
-
"lib/do_notation/monad.rb",
|
25
|
-
"lib/do_notation/monad_plus.rb",
|
26
|
-
"lib/do_notation/monads/array.rb",
|
27
|
-
"lib/do_notation/monads/maybe.rb",
|
28
|
-
"lib/do_notation/monads/simulations.rb",
|
29
|
-
"lib/do_notation/rewriter.rb",
|
30
|
-
"ruby-do-notation.gemspec",
|
31
|
-
"test/array.rb",
|
32
|
-
"test/maybe.rb",
|
33
|
-
"test/monad_plus.rb",
|
34
|
-
"test/simulations.rb",
|
35
|
-
"test/spec_helper.rb",
|
36
|
-
"test/specs.rb"
|
37
|
-
]
|
38
|
-
s.homepage = %q{http://github.com/aanand/do-notation}
|
39
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
40
|
-
s.require_paths = ["lib"]
|
41
|
-
s.rubygems_version = %q{1.3.4}
|
42
|
-
s.summary = %q{Haskell-style monad do-notation for Ruby}
|
43
|
-
s.test_files = [
|
44
|
-
"test/array.rb",
|
45
|
-
"test/maybe.rb",
|
46
|
-
"test/monad_plus.rb",
|
47
|
-
"test/simulations.rb",
|
48
|
-
"test/spec_helper.rb",
|
49
|
-
"test/specs.rb"
|
50
|
-
]
|
51
|
-
|
52
|
-
if s.respond_to? :specification_version then
|
53
|
-
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
54
|
-
s.specification_version = 3
|
55
|
-
|
56
|
-
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
57
|
-
s.add_runtime_dependency(%q<ParseTree>, [">= 0"])
|
58
|
-
s.add_runtime_dependency(%q<ruby2ruby>, ["= 1.1.9"])
|
59
|
-
else
|
60
|
-
s.add_dependency(%q<ParseTree>, [">= 0"])
|
61
|
-
s.add_dependency(%q<ruby2ruby>, ["= 1.1.9"])
|
62
|
-
end
|
63
|
-
else
|
64
|
-
s.add_dependency(%q<ParseTree>, [">= 0"])
|
65
|
-
s.add_dependency(%q<ruby2ruby>, ["= 1.1.9"])
|
66
|
-
end
|
67
|
-
end
|
data/ruby-do-notation.gemspec
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
|
2
|
-
# Gem::Specification for Ruby-do-notation-0.2
|
3
|
-
# Originally generated by Echoe
|
4
|
-
|
5
|
-
Gem::Specification.new do |s|
|
6
|
-
s.name = %q{ruby-do-notation}
|
7
|
-
s.version = "0.2"
|
8
|
-
|
9
|
-
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
|
-
|
11
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
-
s.authors = ["Aanand Prasad"]
|
13
|
-
s.date = %q{2008-06-20}
|
14
|
-
s.description = %q{Haskell-style monad do-notation for Ruby}
|
15
|
-
s.email = %q{aanand.prasad@gmail.com}
|
16
|
-
s.extra_rdoc_files = ["lib/do_notation/monad.rb", "lib/do_notation/monad_plus.rb", "lib/do_notation/monads/array.rb", "lib/do_notation/monads/maybe.rb", "lib/do_notation/monads/simulations.rb", "lib/do_notation/rewriter.rb", "lib/do_notation.rb", "README.markdown"]
|
17
|
-
s.files = ["lib/do_notation/monad.rb", "lib/do_notation/monad_plus.rb", "lib/do_notation/monads/array.rb", "lib/do_notation/monads/maybe.rb", "lib/do_notation/monads/simulations.rb", "lib/do_notation/rewriter.rb", "lib/do_notation.rb", "README.markdown", "test/array.rb", "test/maybe.rb", "test/monad_plus.rb", "test/simulations.rb", "test/spec_helper.rb", "test/specs.rb", "Manifest", "ruby-do-notation.gemspec"]
|
18
|
-
s.has_rdoc = true
|
19
|
-
s.homepage = %q{http://github.com/aanand/ruby-do-notation/tree/master}
|
20
|
-
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Ruby-do-notation", "--main", "README.markdown"]
|
21
|
-
s.require_paths = ["lib"]
|
22
|
-
s.rubyforge_project = %q{ruby-do-notation}
|
23
|
-
s.rubygems_version = %q{1.1.0}
|
24
|
-
s.summary = %q{Haskell-style monad do-notation for Ruby}
|
25
|
-
|
26
|
-
s.add_dependency(%q<ParseTree>, [">= 0"])
|
27
|
-
s.add_dependency(%q<ruby2ruby>, [">= 0"])
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
# # Original Rakefile source (requires the Echoe gem):
|
32
|
-
#
|
33
|
-
# require 'spec/rake/spectask'
|
34
|
-
#
|
35
|
-
# spec_list = FileList['test/*.rb']
|
36
|
-
#
|
37
|
-
# task :default => :test
|
38
|
-
#
|
39
|
-
# desc "Run all specs"
|
40
|
-
# Spec::Rake::SpecTask.new('spec') do |t|
|
41
|
-
# t.spec_files = spec_list
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# desc "Run all specs with RCov"
|
45
|
-
# Spec::Rake::SpecTask.new('rcov') do |t|
|
46
|
-
# t.spec_files = spec_list
|
47
|
-
# t.rcov = true
|
48
|
-
# end
|
49
|
-
#
|
50
|
-
# require 'echoe'
|
51
|
-
#
|
52
|
-
# Echoe.new('ruby-do-notation') do |p|
|
53
|
-
# p.author = 'Aanand Prasad'
|
54
|
-
# p.summary = 'Haskell-style monad do-notation for Ruby'
|
55
|
-
# p.email = 'aanand.prasad@gmail.com'
|
56
|
-
# p.url = 'http://github.com/aanand/ruby-do-notation/tree/master'
|
57
|
-
# p.version = '0.2'
|
58
|
-
# p.dependencies = ['ParseTree', 'ruby2ruby']
|
59
|
-
# end
|
data/test/monad_plus.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
-
require 'do_notation/monads/array'
|
3
|
-
require 'do_notation/monads/maybe'
|
4
|
-
|
5
|
-
describe "MonadPlus:" do
|
6
|
-
specify "mzero >>= f = mzero" do
|
7
|
-
Array.mzero.bind{ |x| unit(x+1) }.should == Array.mzero
|
8
|
-
Maybe.mzero.bind{ |x| unit(x+1) }.should == Maybe.mzero
|
9
|
-
end
|
10
|
-
|
11
|
-
specify "v >> mzero = mzero" do
|
12
|
-
([1,2,3] >> Array.mzero).should == Array.mzero
|
13
|
-
(Maybe.unit(1) >> Maybe.mzero).should == Maybe.mzero
|
14
|
-
end
|
15
|
-
|
16
|
-
specify "mzero `mplus` m = m" do
|
17
|
-
Array.mzero.mplus([1,2,3]).should == [1,2,3]
|
18
|
-
Maybe.mzero.mplus(Maybe.unit(1)).should == Maybe.unit(1)
|
19
|
-
end
|
20
|
-
|
21
|
-
specify "m `mplus` mzero = m" do
|
22
|
-
[1,2,3].mplus(Array.mzero).should == [1,2,3]
|
23
|
-
Maybe.unit(1).mplus(Maybe.mzero).should == Maybe.unit(1)
|
24
|
-
end
|
25
|
-
|
26
|
-
specify "guard() prunes the execution tree" do
|
27
|
-
array = Array.run do
|
28
|
-
x <- [0,1,2,3]
|
29
|
-
y <- [0,1,2,3]
|
30
|
-
guard(x + y == 3)
|
31
|
-
|
32
|
-
unit([x,y])
|
33
|
-
end
|
34
|
-
|
35
|
-
array.should == [[0, 3], [1, 2], [2, 1], [3, 0]]
|
36
|
-
end
|
37
|
-
end
|
data/test/simulations.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
-
require 'do_notation/monads/simulations'
|
3
|
-
|
4
|
-
roll_3d6 = proc do
|
5
|
-
d1 <- rand(6)
|
6
|
-
d2 <- rand(6)
|
7
|
-
d3 <- rand(6)
|
8
|
-
|
9
|
-
unit(1+d1 + 1+d2 + 1+d3)
|
10
|
-
end
|
11
|
-
|
12
|
-
roll_3d6_b = proc do
|
13
|
-
d1 <- rand(6)
|
14
|
-
d2 <- rand(6)
|
15
|
-
d3 <- rand(6)
|
16
|
-
|
17
|
-
if [d1, d2, d3].include?(5)
|
18
|
-
run do
|
19
|
-
d4 <- rand(6)
|
20
|
-
unit(1+d1 + 1+d2 + 1+d3 + 1+d4)
|
21
|
-
end
|
22
|
-
else
|
23
|
-
unit(1+d1 + 1+d2 + 1+d3)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
describe "Simulation" do
|
28
|
-
specify "generates correct answers" do
|
29
|
-
Simulation.run(&roll_3d6).play.should == 9
|
30
|
-
Simulation.run(&roll_3d6_b).play.should == 9
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
describe "Distribution" do
|
35
|
-
specify "generates correct answers" do
|
36
|
-
Distribution.run(&roll_3d6).play.inspect.should == "[[3, 0.00462962962962963], [4, 0.0138888888888889], [5, 0.0277777777777778], [6, 0.0462962962962963], [7, 0.0694444444444444], [8, 0.0972222222222222], [9, 0.115740740740741], [10, 0.125], [11, 0.125], [12, 0.115740740740741], [13, 0.0972222222222222], [14, 0.0694444444444444], [15, 0.0462962962962963], [16, 0.0277777777777778], [17, 0.0138888888888889], [18, 0.00462962962962963]]"
|
37
|
-
Distribution.run(&roll_3d6_b).play.inspect.should == "[[3, 0.00462962962962963], [4, 0.0138888888888889], [5, 0.0277777777777778], [6, 0.0462962962962963], [7, 0.0694444444444444], [8, 0.0833333333333333], [9, 0.0902777777777777], [10, 0.0902777777777778], [11, 0.0833333333333333], [12, 0.0694444444444445], [13, 0.0625000000000001], [14, 0.0601851851851853], [15, 0.0578703703703705], [16, 0.0555555555555557], [17, 0.0532407407407408], [18, 0.0462962962962964], [19, 0.0354938271604938], [20, 0.0239197530864198], [21, 0.0146604938271605], [22, 0.00771604938271605], [23, 0.00308641975308642], [24, 0.000771604938271605]]"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe "Expectation" do
|
42
|
-
specify "generates correct answers" do
|
43
|
-
(1..21).collect { |x| Expectation.run(&roll_3d6).play(x) }.inspect.should == "[0.0, 0.0, 0.00462962962962963, 0.0138888888888889, 0.0277777777777778, 0.0462962962962963, 0.0694444444444444, 0.0972222222222222, 0.115740740740741, 0.125, 0.125, 0.115740740740741, 0.0972222222222222, 0.0694444444444444, 0.0462962962962963, 0.0277777777777778, 0.0138888888888889, 0.00462962962962963, 0.0, 0.0, 0.0]"
|
44
|
-
(1..21).collect { |x| Expectation.run(&roll_3d6_b).play(x) }.inspect.should == "[0.0, 0.0, 0.00462962962962963, 0.0138888888888889, 0.0277777777777778, 0.0462962962962963, 0.0694444444444444, 0.0833333333333333, 0.0902777777777778, 0.0902777777777777, 0.0833333333333333, 0.0694444444444444, 0.0625, 0.0601851851851852, 0.0578703703703704, 0.0555555555555556, 0.0532407407407407, 0.0462962962962963, 0.0354938271604938, 0.0239197530864197, 0.0146604938271605]"
|
45
|
-
end
|
46
|
-
end
|