GTof-ruby-do-notation 0.2
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/Manifest +15 -0
- data/README.markdown +34 -0
- data/Rakefile +27 -0
- data/lib/do_notation.rb +9 -0
- data/lib/do_notation/monad.rb +101 -0
- data/lib/do_notation/monad_plus.rb +9 -0
- data/lib/do_notation/monads/array.rb +21 -0
- data/lib/do_notation/monads/maybe.rb +31 -0
- data/lib/do_notation/monads/simulations.rb +117 -0
- data/lib/do_notation/rewriter.rb +63 -0
- data/ruby-do-notation.gemspec +38 -0
- data/test/array.rb +18 -0
- data/test/maybe.rb +26 -0
- data/test/monad_plus.rb +37 -0
- data/test/simulations.rb +46 -0
- data/test/spec_helper.rb +1 -0
- data/test/specs.rb +66 -0
- metadata +107 -0
data/Manifest
ADDED
@@ -0,0 +1,15 @@
|
|
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.markdown
|
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/README.markdown
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
Haskell-style monad do-notation for Ruby
|
2
|
+
========================================
|
3
|
+
|
4
|
+
Example:
|
5
|
+
|
6
|
+
class Array
|
7
|
+
include Monad
|
8
|
+
|
9
|
+
def self.unit x
|
10
|
+
[x]
|
11
|
+
end
|
12
|
+
|
13
|
+
def bind &f
|
14
|
+
map(&f).inject([]){ |a,b| a+b }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Array.run do
|
19
|
+
x <- ["first", "second"]
|
20
|
+
y <- ["once", "twice"]
|
21
|
+
|
22
|
+
unit("#{x} cousin #{y} removed")
|
23
|
+
end
|
24
|
+
|
25
|
+
The above code returns the array:
|
26
|
+
|
27
|
+
["first cousin once removed",
|
28
|
+
"first cousin twice removed",
|
29
|
+
"second cousin once removed",
|
30
|
+
"second cousin twice removed"]
|
31
|
+
|
32
|
+
For more examples, see the test suite.
|
33
|
+
|
34
|
+
By Aanand Prasad (aanand.prasad@gmail.com)
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
|
3
|
+
spec_list = FileList['test/*.rb']
|
4
|
+
|
5
|
+
task :default => :test
|
6
|
+
|
7
|
+
desc "Run all specs"
|
8
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
9
|
+
t.spec_files = spec_list
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Run all specs with RCov"
|
13
|
+
Spec::Rake::SpecTask.new('rcov') do |t|
|
14
|
+
t.spec_files = spec_list
|
15
|
+
t.rcov = true
|
16
|
+
end
|
17
|
+
|
18
|
+
require 'echoe'
|
19
|
+
|
20
|
+
Echoe.new('ruby-do-notation') do |p|
|
21
|
+
p.author = 'Aanand Prasad'
|
22
|
+
p.summary = 'Haskell-style monad do-notation for Ruby'
|
23
|
+
p.email = 'aanand.prasad@gmail.com'
|
24
|
+
p.url = 'http://github.com/aanand/ruby-do-notation/tree/master'
|
25
|
+
p.version = '0.2'
|
26
|
+
p.dependencies = ['ParseTree', 'ruby2ruby']
|
27
|
+
end
|
data/lib/do_notation.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# Modeled after Andrzej Filinski's article "Representing
|
2
|
+
# Monads" at POPL'94, and a Scheme implementation of it.
|
3
|
+
# http://citeseer.ist.psu.edu/filinski94representing.html
|
4
|
+
|
5
|
+
module ShiftReset
|
6
|
+
@@metacont = lambda { |x|
|
7
|
+
raise RuntimeError, "You forgot the top-level reset..."
|
8
|
+
}
|
9
|
+
|
10
|
+
def reset(&block)
|
11
|
+
mc = @@metacont
|
12
|
+
callcc { |k|
|
13
|
+
@@metacont = lambda { |v|
|
14
|
+
@@metacont = mc
|
15
|
+
k.call v
|
16
|
+
}
|
17
|
+
x = block.call
|
18
|
+
@@metacont.call x
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
def shift(&block)
|
23
|
+
callcc { |k|
|
24
|
+
@@metacont.call block.call(lambda { |*v| reset { k.call *v } })
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
include ShiftReset
|
30
|
+
|
31
|
+
module Monad
|
32
|
+
module ClassMethods
|
33
|
+
def run &block
|
34
|
+
eval(ruby_for(block), block).call
|
35
|
+
end
|
36
|
+
|
37
|
+
def ruby_for block
|
38
|
+
@cached_ruby ||= {}
|
39
|
+
@cached_ruby[block.to_s] ||= "#{self.name}.instance_eval { #{Ruby2Ruby.new.process(Rewriter.new.process(block.to_method.to_sexp)[2])} }"
|
40
|
+
end
|
41
|
+
|
42
|
+
def sequence l
|
43
|
+
if l.empty?
|
44
|
+
unit([])
|
45
|
+
else
|
46
|
+
run do
|
47
|
+
x <- l[0]
|
48
|
+
q <- sequence(l[1..-1])
|
49
|
+
unit([x] + q)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def foldM(i,l, &f)
|
55
|
+
if l.empty?
|
56
|
+
unit(i)
|
57
|
+
else
|
58
|
+
run do
|
59
|
+
x <- f.call(i,l[0])
|
60
|
+
foldM(x,l[1..-1]) { |a,b| f.call(a,b) }
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def fmap f
|
66
|
+
lambda { |m| m.bind { |y| unit(f.call(y)) } }
|
67
|
+
end
|
68
|
+
|
69
|
+
def reify &t
|
70
|
+
reset { unit( t.call() ) }
|
71
|
+
end
|
72
|
+
|
73
|
+
def reflect m
|
74
|
+
shift { |k| m.bind { |v| k.call(v) } }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def self.included m
|
79
|
+
m.extend ClassMethods
|
80
|
+
end
|
81
|
+
|
82
|
+
def bind_const &block
|
83
|
+
bind { |_| block.call() }
|
84
|
+
end
|
85
|
+
|
86
|
+
def >> n
|
87
|
+
bind_const { n }
|
88
|
+
end
|
89
|
+
|
90
|
+
def joinM
|
91
|
+
bind { |n| n }
|
92
|
+
end
|
93
|
+
|
94
|
+
# I don't know why this reflect doesn't work
|
95
|
+
# the class method seems to be the same and work
|
96
|
+
# the evaluation moment of the shift in a class method
|
97
|
+
# and in a method may be different
|
98
|
+
def reflect_that_doesnt_work
|
99
|
+
shift { |k| bind { |v| k.call(v) } }
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'do_notation/monad_plus'
|
2
|
+
|
3
|
+
class Array
|
4
|
+
include Monad
|
5
|
+
|
6
|
+
def self.unit x
|
7
|
+
[x]
|
8
|
+
end
|
9
|
+
|
10
|
+
def bind &f
|
11
|
+
map(&f).inject([]){ |a,b| a+b }
|
12
|
+
end
|
13
|
+
|
14
|
+
extend MonadPlus
|
15
|
+
|
16
|
+
def self.mzero
|
17
|
+
[]
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :mplus, :+
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'do_notation/monad_plus'
|
2
|
+
|
3
|
+
class Maybe < Struct.new(:value)
|
4
|
+
include Monad
|
5
|
+
|
6
|
+
def self.unit value
|
7
|
+
self.new(value)
|
8
|
+
end
|
9
|
+
|
10
|
+
def bind &f
|
11
|
+
if value.nil?
|
12
|
+
self
|
13
|
+
else
|
14
|
+
f.call(value)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
extend MonadPlus
|
19
|
+
|
20
|
+
def self.mzero
|
21
|
+
unit(nil)
|
22
|
+
end
|
23
|
+
|
24
|
+
def mplus m
|
25
|
+
if value.nil?
|
26
|
+
m
|
27
|
+
else
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# converted from Mauricio Fernandez's "Warm fuzzy things for random simulations":
|
2
|
+
# http://eigenclass.org/hiki/warm-fuzzy-things-for-random-simulations
|
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
|
+
|
5
|
+
module PRNG
|
6
|
+
def next_state(s); (69069 * s + 5) % (2**32) end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Simulation
|
10
|
+
extend PRNG
|
11
|
+
include Monad
|
12
|
+
|
13
|
+
attr_reader :f
|
14
|
+
|
15
|
+
def initialize(&b)
|
16
|
+
@f = b
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.unit(x)
|
20
|
+
new { |s| [x, s] }
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.rand(n)
|
24
|
+
self.new do |s|
|
25
|
+
[s.abs % n, next_state(s)]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def bind(&b)
|
30
|
+
self.class.new do |s|
|
31
|
+
x, s = @f.call(s)
|
32
|
+
b.call(x).f.call(s)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def play(s = 12345)
|
37
|
+
@f.call(s).first
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Distribution
|
42
|
+
extend PRNG
|
43
|
+
include Monad
|
44
|
+
|
45
|
+
attr_reader :a
|
46
|
+
|
47
|
+
def initialize(a)
|
48
|
+
@a = a
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.wrap(a)
|
52
|
+
new(a)
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.unit(x)
|
56
|
+
wrap [[x, 1.0]]
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.rand(n)
|
60
|
+
p = 1.0 / n
|
61
|
+
new((0...n).map{|i| [i, p]})
|
62
|
+
end
|
63
|
+
|
64
|
+
def bind(&b)
|
65
|
+
if @a.empty?
|
66
|
+
self.class.wrap([])
|
67
|
+
else
|
68
|
+
x, p = @a[0]
|
69
|
+
self.class.wrap(mulp(p, b.call(x)) + self.class.new(@a[1..-1]).bind(&b).a)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def play
|
74
|
+
h = Hash.new{|h, k| h[k] = 0.0}
|
75
|
+
@a.each{|x, p| h[x] += p}
|
76
|
+
h.to_a.sort_by{|x,| x}
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def mulp(p, l)
|
81
|
+
l.a.map{|x, p1| [x, p * p1]}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
class Expectation
|
86
|
+
extend PRNG
|
87
|
+
include Monad
|
88
|
+
|
89
|
+
attr_reader :f
|
90
|
+
|
91
|
+
def initialize(&b)
|
92
|
+
@f = b
|
93
|
+
end
|
94
|
+
|
95
|
+
def self.wrap(&proc)
|
96
|
+
new(&proc)
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.unit(x)
|
100
|
+
wrap{ |f| f.call(x) }
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.rand(n)
|
104
|
+
wrap do |k|
|
105
|
+
sum = (0..n-1).map{|x| k.call(x)}.inject{|s,x| s+x}
|
106
|
+
1.0 * sum / n
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def bind(&b)
|
111
|
+
self.class.wrap{|k| @f.call(lambda{|x| b.call(x).f.call(k)}) }
|
112
|
+
end
|
113
|
+
|
114
|
+
def play(x)
|
115
|
+
@f.call(lambda{|x1| x1 == x ? 1.0 : 0.0})
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
class Rewriter < SexpProcessor
|
2
|
+
def process_bmethod exp
|
3
|
+
type = exp.shift
|
4
|
+
|
5
|
+
exp.shift # throw away arguments
|
6
|
+
|
7
|
+
body = process(exp.shift)
|
8
|
+
|
9
|
+
if body[0] == :block
|
10
|
+
body.shift
|
11
|
+
else
|
12
|
+
body = s([*body])
|
13
|
+
end
|
14
|
+
|
15
|
+
s(:iter, s(:fcall, :proc), nil, *rewrite_assignments(body))
|
16
|
+
end
|
17
|
+
|
18
|
+
def rewrite_assignments exp
|
19
|
+
return [] if exp.empty?
|
20
|
+
|
21
|
+
head = exp.shift
|
22
|
+
|
23
|
+
if head.first == :call and head[1].first == :vcall and head[2] == :< and head[3].first == :array and head[3][1].last == :-@
|
24
|
+
var_name = head[1][1]
|
25
|
+
expression = head[3][1][1]
|
26
|
+
|
27
|
+
body = rewrite_assignments(exp)
|
28
|
+
|
29
|
+
if body.first.is_a? Symbol
|
30
|
+
body = [s(*body)]
|
31
|
+
end
|
32
|
+
|
33
|
+
[s(:iter,
|
34
|
+
s(:call, process(expression), :bind),
|
35
|
+
s(:dasgn_curr, var_name),
|
36
|
+
*body)]
|
37
|
+
elsif exp.empty?
|
38
|
+
[process(head)]
|
39
|
+
else
|
40
|
+
[s(:iter, s(:call, process(head) , :bind_const), nil ,
|
41
|
+
*rewrite_assignments(exp)) ]
|
42
|
+
end
|
43
|
+
end
|
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}")
|
57
|
+
end
|
58
|
+
|
59
|
+
str << ')'
|
60
|
+
|
61
|
+
str
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = %q{ruby-do-notation}
|
3
|
+
s.version = "0.2"
|
4
|
+
|
5
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
6
|
+
s.authors = ["Aanand Prasad"]
|
7
|
+
s.date = %q{2009-02-03}
|
8
|
+
s.description = %q{Haskell-style monad do-notation for Ruby}
|
9
|
+
s.email = %q{aanand.prasad@gmail.com}
|
10
|
+
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"]
|
11
|
+
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", "Rakefile"]
|
12
|
+
s.has_rdoc = true
|
13
|
+
s.homepage = %q{http://github.com/aanand/ruby-do-notation/tree/master}
|
14
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Ruby-do-notation", "--main", "README.markdown"]
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.rubyforge_project = %q{ruby-do-notation}
|
17
|
+
s.rubygems_version = %q{1.2.0}
|
18
|
+
s.summary = %q{Haskell-style monad do-notation for Ruby}
|
19
|
+
|
20
|
+
if s.respond_to? :specification_version then
|
21
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
22
|
+
s.specification_version = 2
|
23
|
+
|
24
|
+
if current_version >= 3 then
|
25
|
+
s.add_runtime_dependency(%q<ParseTree>, [">= 0"])
|
26
|
+
s.add_runtime_dependency(%q<ruby2ruby>, [">= 0"])
|
27
|
+
s.add_development_dependency(%q<echoe>, [">= 0"])
|
28
|
+
else
|
29
|
+
s.add_dependency(%q<ParseTree>, [">= 0"])
|
30
|
+
s.add_dependency(%q<ruby2ruby>, [">= 0"])
|
31
|
+
s.add_dependency(%q<echoe>, [">= 0"])
|
32
|
+
end
|
33
|
+
else
|
34
|
+
s.add_dependency(%q<ParseTree>, [">= 0"])
|
35
|
+
s.add_dependency(%q<ruby2ruby>, [">= 0"])
|
36
|
+
s.add_dependency(%q<echoe>, [">= 0"])
|
37
|
+
end
|
38
|
+
end
|
data/test/array.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
require 'do_notation/monads/array'
|
3
|
+
|
4
|
+
describe "Array:" do
|
5
|
+
specify "all results are calculated and concatenated" do
|
6
|
+
array = Array.run do
|
7
|
+
x <- ["first", "second"]
|
8
|
+
y <- ["once", "twice"]
|
9
|
+
|
10
|
+
unit("#{x} cousin #{y} removed")
|
11
|
+
end
|
12
|
+
|
13
|
+
array.should == ["first cousin once removed",
|
14
|
+
"first cousin twice removed",
|
15
|
+
"second cousin once removed",
|
16
|
+
"second cousin twice removed"]
|
17
|
+
end
|
18
|
+
end
|
data/test/maybe.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
require 'do_notation/monads/maybe'
|
3
|
+
|
4
|
+
describe "Maybe:" do
|
5
|
+
specify "one or more nils results in nil" do
|
6
|
+
maybe = Maybe.run do
|
7
|
+
x <- unit(1)
|
8
|
+
y <- unit(nil)
|
9
|
+
|
10
|
+
unit(x+y)
|
11
|
+
end
|
12
|
+
|
13
|
+
maybe.value.should == nil
|
14
|
+
end
|
15
|
+
|
16
|
+
specify "all non-nil results in complete calculation" do
|
17
|
+
maybe = Maybe.run do
|
18
|
+
x <- unit(1)
|
19
|
+
y <- unit(2)
|
20
|
+
|
21
|
+
unit(x+y)
|
22
|
+
end
|
23
|
+
|
24
|
+
maybe.value.should == 3
|
25
|
+
end
|
26
|
+
end
|
data/test/monad_plus.rb
ADDED
@@ -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
|
+
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
ADDED
@@ -0,0 +1,46 @@
|
|
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
|
data/test/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), %w(.. lib do_notation))
|
data/test/specs.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
require 'do_notation/monads/array'
|
3
|
+
|
4
|
+
describe "Monad.run" do
|
5
|
+
specify "should have lexical scope" do
|
6
|
+
foo = "cousin"
|
7
|
+
bar = "removed"
|
8
|
+
|
9
|
+
array = Array.run do
|
10
|
+
x <- ["first", "second"]
|
11
|
+
y <- ["once", "twice"]
|
12
|
+
|
13
|
+
unit("#{x} #{foo} #{y} #{bar}")
|
14
|
+
end
|
15
|
+
|
16
|
+
array.should == ["first cousin once removed",
|
17
|
+
"first cousin twice removed",
|
18
|
+
"second cousin once removed",
|
19
|
+
"second cousin twice removed"]
|
20
|
+
end
|
21
|
+
|
22
|
+
specify "should be nestable" do
|
23
|
+
array = Array.run do
|
24
|
+
x <- run do
|
25
|
+
a <- ['A','a']
|
26
|
+
b <- ['B','b']
|
27
|
+
|
28
|
+
unit(a+b)
|
29
|
+
end
|
30
|
+
|
31
|
+
y <- run do
|
32
|
+
c <- ['C','c']
|
33
|
+
d <- ['D','d']
|
34
|
+
|
35
|
+
unit(c+d)
|
36
|
+
end
|
37
|
+
|
38
|
+
unit(x+y)
|
39
|
+
end
|
40
|
+
|
41
|
+
array.should == ["ABCD", "ABCd", "ABcD", "ABcd",
|
42
|
+
"AbCD", "AbCd", "AbcD", "Abcd",
|
43
|
+
"aBCD", "aBCd", "aBcD", "aBcd",
|
44
|
+
"abCD", "abCd", "abcD", "abcd"]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe 'Monad#>>' do
|
49
|
+
specify "should compose two values, discarding the first" do
|
50
|
+
([1] >> [2]).should == [2]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
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
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: GTof-ruby-do-notation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "0.2"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aanand Prasad
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-02-03 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ParseTree
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: "0"
|
23
|
+
version:
|
24
|
+
- !ruby/object:Gem::Dependency
|
25
|
+
name: ruby2ruby
|
26
|
+
version_requirement:
|
27
|
+
version_requirements: !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ">="
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: "0"
|
32
|
+
version:
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: echoe
|
35
|
+
version_requirement:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: "0"
|
41
|
+
version:
|
42
|
+
description: Haskell-style monad do-notation for Ruby
|
43
|
+
email: aanand.prasad@gmail.com
|
44
|
+
executables: []
|
45
|
+
|
46
|
+
extensions: []
|
47
|
+
|
48
|
+
extra_rdoc_files:
|
49
|
+
- lib/do_notation/monad.rb
|
50
|
+
- lib/do_notation/monad_plus.rb
|
51
|
+
- lib/do_notation/monads/array.rb
|
52
|
+
- lib/do_notation/monads/maybe.rb
|
53
|
+
- lib/do_notation/monads/simulations.rb
|
54
|
+
- lib/do_notation/rewriter.rb
|
55
|
+
- lib/do_notation.rb
|
56
|
+
- README.markdown
|
57
|
+
files:
|
58
|
+
- lib/do_notation/monad.rb
|
59
|
+
- lib/do_notation/monad_plus.rb
|
60
|
+
- lib/do_notation/monads/array.rb
|
61
|
+
- lib/do_notation/monads/maybe.rb
|
62
|
+
- lib/do_notation/monads/simulations.rb
|
63
|
+
- lib/do_notation/rewriter.rb
|
64
|
+
- lib/do_notation.rb
|
65
|
+
- README.markdown
|
66
|
+
- test/array.rb
|
67
|
+
- test/maybe.rb
|
68
|
+
- test/monad_plus.rb
|
69
|
+
- test/simulations.rb
|
70
|
+
- test/spec_helper.rb
|
71
|
+
- test/specs.rb
|
72
|
+
- Manifest
|
73
|
+
- ruby-do-notation.gemspec
|
74
|
+
- Rakefile
|
75
|
+
has_rdoc: true
|
76
|
+
homepage: http://github.com/aanand/ruby-do-notation/tree/master
|
77
|
+
post_install_message:
|
78
|
+
rdoc_options:
|
79
|
+
- --line-numbers
|
80
|
+
- --inline-source
|
81
|
+
- --title
|
82
|
+
- Ruby-do-notation
|
83
|
+
- --main
|
84
|
+
- README.markdown
|
85
|
+
require_paths:
|
86
|
+
- lib
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: "0"
|
92
|
+
version:
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: "1.2"
|
98
|
+
version:
|
99
|
+
requirements: []
|
100
|
+
|
101
|
+
rubyforge_project: ruby-do-notation
|
102
|
+
rubygems_version: 1.2.0
|
103
|
+
signing_key:
|
104
|
+
specification_version: 2
|
105
|
+
summary: Haskell-style monad do-notation for Ruby
|
106
|
+
test_files: []
|
107
|
+
|