do_notation 0.2.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 +5 -0
- data/Manifest +15 -0
- data/README.rdoc +42 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/do-notation.gemspec +67 -0
- data/lib/do_notation/monad.rb +24 -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/lib/do_notation.rb +12 -0
- data/ruby-do-notation.gemspec +59 -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 +98 -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.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/README.rdoc
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
= Haskell-style monad do-notation for Ruby
|
2
|
+
|
3
|
+
Example:
|
4
|
+
|
5
|
+
require 'do_notation'
|
6
|
+
|
7
|
+
class Array
|
8
|
+
include Monad
|
9
|
+
|
10
|
+
def self.unit x
|
11
|
+
[x]
|
12
|
+
end
|
13
|
+
|
14
|
+
def bind &f
|
15
|
+
map(&f).inject([]){ |a,b| a+b }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Array.run do
|
20
|
+
x <- ["first", "second"]
|
21
|
+
y <- ["once", "twice"]
|
22
|
+
|
23
|
+
unit("#{x} cousin #{y} removed")
|
24
|
+
end
|
25
|
+
|
26
|
+
The above code returns the array:
|
27
|
+
|
28
|
+
["first cousin once removed",
|
29
|
+
"first cousin twice removed",
|
30
|
+
"second cousin once removed",
|
31
|
+
"second cousin twice removed"]
|
32
|
+
|
33
|
+
For more examples, see the test suite.
|
34
|
+
|
35
|
+
== Installation
|
36
|
+
|
37
|
+
There's a gem hosted on Github, so follow the setup instructions
|
38
|
+
at http://gems.github.com and then run:
|
39
|
+
|
40
|
+
sudo gem install aanand-ruby-do-notation
|
41
|
+
|
42
|
+
By Aanand Prasad (aanand.prasad@gmail.com)
|
data/Rakefile
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec/rake/spectask'
|
2
|
+
|
3
|
+
spec_list = FileList['test/*.rb']
|
4
|
+
|
5
|
+
task :default => :spec
|
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
|
+
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/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/do-notation.gemspec
ADDED
@@ -0,0 +1,67 @@
|
|
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
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Monad
|
2
|
+
module ClassMethods
|
3
|
+
def run &block
|
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() }
|
15
|
+
end
|
16
|
+
|
17
|
+
def >> n
|
18
|
+
bind_const { n }
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.included m
|
22
|
+
m.extend ClassMethods
|
23
|
+
end
|
24
|
+
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
|
data/lib/do_notation.rb
ADDED
@@ -0,0 +1,59 @@
|
|
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/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,98 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: do_notation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Aanand Prasad
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-09-29 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ParseTree
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: ruby2ruby
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.1.9
|
34
|
+
version:
|
35
|
+
description: Haskell-style monad do-notation for Ruby
|
36
|
+
email: aanand.prasad@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- Manifest
|
46
|
+
- README.rdoc
|
47
|
+
- Rakefile
|
48
|
+
- VERSION
|
49
|
+
- do-notation.gemspec
|
50
|
+
- lib/do_notation.rb
|
51
|
+
- lib/do_notation/monad.rb
|
52
|
+
- lib/do_notation/monad_plus.rb
|
53
|
+
- lib/do_notation/monads/array.rb
|
54
|
+
- lib/do_notation/monads/maybe.rb
|
55
|
+
- lib/do_notation/monads/simulations.rb
|
56
|
+
- lib/do_notation/rewriter.rb
|
57
|
+
- ruby-do-notation.gemspec
|
58
|
+
- test/array.rb
|
59
|
+
- test/maybe.rb
|
60
|
+
- test/monad_plus.rb
|
61
|
+
- test/simulations.rb
|
62
|
+
- test/spec_helper.rb
|
63
|
+
- test/specs.rb
|
64
|
+
has_rdoc: true
|
65
|
+
homepage: http://github.com/aanand/do_notation
|
66
|
+
licenses: []
|
67
|
+
|
68
|
+
post_install_message:
|
69
|
+
rdoc_options:
|
70
|
+
- --charset=UTF-8
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
version:
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "0"
|
84
|
+
version:
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project:
|
88
|
+
rubygems_version: 1.3.4
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: Haskell-style monad do-notation for Ruby
|
92
|
+
test_files:
|
93
|
+
- test/array.rb
|
94
|
+
- test/maybe.rb
|
95
|
+
- test/monad_plus.rb
|
96
|
+
- test/simulations.rb
|
97
|
+
- test/spec_helper.rb
|
98
|
+
- test/specs.rb
|