do_notation 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|