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 CHANGED
@@ -3,3 +3,4 @@ coverage
3
3
  doc
4
4
  *.tmproj
5
5
  *.gem
6
+ Gemfile.lock
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
5
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+ gemspec
3
+
@@ -1,4 +1,5 @@
1
- = Haskell-style monad do-notation for Ruby
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([]){ |a,b| a+b }
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
- == Installation
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
- sudo gem install aanand-ruby-do-notation
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 'spec/rake/spectask'
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
- Spec::Rake::SpecTask.new('spec') do |t|
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
- Spec::Rake::SpecTask.new('rcov') do |t|
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
@@ -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
- $: << File.dirname(__FILE__)
1
+ require 'sourcify'
2
2
 
3
- require 'rubygems'
3
+ module DoNotation
4
+ def self.run(klass, &block)
5
+ eval(ruby_for(klass, block), block.binding).call
6
+ end
4
7
 
5
- gem 'ruby2ruby', '1.1.9'
8
+ def self.ruby_for(klass, block)
9
+ "#{klass.name}.instance_eval { #{ruby_for_block(block)} }"
10
+ end
6
11
 
7
- require 'parse_tree'
8
- require 'sexp_processor'
9
- require 'ruby2ruby'
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
+
@@ -1,24 +1,13 @@
1
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() }
2
+ def run &block
3
+ DoNotation.run(self, &block)
15
4
  end
16
5
 
17
- def >> n
18
- bind_const { n }
6
+ def bind_const value, &block
7
+ bind(value) { block.call }
19
8
  end
20
9
 
21
- def self.included m
22
- m.extend ClassMethods
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
- include Monad
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([]){ |a,b| a+b }
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
- alias_method :mplus, :+
20
+
21
+ def self.mplus(a, b)
22
+ a + b
23
+ end
21
24
  end
@@ -0,0 +1,16 @@
1
+ require 'do_notation'
2
+
3
+ module Identity
4
+ extend Monad
5
+
6
+ class << self
7
+ def unit(obj)
8
+ obj
9
+ end
10
+
11
+ def bind(obj, &fn)
12
+ fn.call(obj)
13
+ end
14
+ end
15
+ end
16
+
@@ -1,31 +1,32 @@
1
+ require 'do_notation'
1
2
  require 'do_notation/monad_plus'
2
3
 
3
- class Maybe < Struct.new(:value)
4
- include Monad
5
-
4
+ class Maybe
5
+ extend Monad
6
+
6
7
  def self.unit value
7
- self.new(value)
8
+ value
8
9
  end
9
-
10
- def bind &f
10
+
11
+ def self.bind value, &f
11
12
  if value.nil?
12
- self
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 m
25
- if value.nil?
26
- m
24
+
25
+ def self.mplus a, b
26
+ if b.nil?
27
+ a
27
28
  else
28
- self
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
- module PRNG
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
- include Monad
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.class.new do |s|
31
- x, s = @f.call(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
- include Monad
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 @a.empty?
66
- self.class.wrap([])
66
+ def self.bind(distribution, &b)
67
+ if distribution.a.empty?
68
+ wrap([])
67
69
  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
+ 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
- include Monad
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
- self.class.wrap{|k| @f.call(lambda{|x| b.call(x).f.call(k)}) }
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)
@@ -1,63 +1,45 @@
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
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
- body = s([*body])
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
- 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]
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, process(expression), :bind),
35
- s(:dasgn_curr, var_name),
33
+ s(:call, nil, :bind, s(:arglist, expression)),
34
+ s(:lasgn, var_name),
36
35
  *body)]
37
36
  elsif exp.empty?
38
- [process(head)]
37
+ [head]
39
38
  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}")
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
@@ -6,8 +6,8 @@ describe "Array:" do
6
6
  array = Array.run do
7
7
  x <- ["first", "second"]
8
8
  y <- ["once", "twice"]
9
-
10
- unit("#{x} cousin #{y} removed")
9
+
10
+ ["#{x} cousin #{y} removed"]
11
11
  end
12
12
 
13
13
  array.should == ["first cousin once removed",
@@ -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
- maybe = Maybe.run do
7
- x <- unit(1)
8
- y <- unit(nil)
9
-
10
- unit(x+y)
6
+ value = Maybe.run do
7
+ x <- 1
8
+ y <- nil
9
+
10
+ x + y
11
11
  end
12
12
 
13
- maybe.value.should == nil
13
+ value.should == nil
14
14
  end
15
15
 
16
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)
17
+ value = Maybe.run do
18
+ x <- 1
19
+ y <- 2
20
+
21
+ x + y
22
22
  end
23
-
24
- maybe.value.should == 3
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
- unit("#{x} #{foo} #{y} #{bar}")
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
- unit(a+b)
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
- unit(c+d)
49
+ [c+d]
36
50
  end
37
51
 
38
- unit(x+y)
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 'Monad#>>' do
62
+ describe 'compose' do
49
63
  specify "should compose two values, discarding the first" do
50
- ([1] >> [2]).should == [2]
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
- version: 0.2.0
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: 2009-09-29 00:00:00 -04:00
13
- default_executable:
18
+ date: 2011-11-14 00:00:00 Z
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
- name: ParseTree
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
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
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
- version:
46
+ type: :development
47
+ version_requirements: *id002
25
48
  - !ruby/object:Gem::Dependency
26
- name: ruby2ruby
27
- type: :runtime
28
- version_requirement:
29
- version_requirements: !ruby/object:Gem::Requirement
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
- version: 1.1.9
34
- version:
35
- description: Haskell-style monad do-notation for Ruby
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
- - README.rdoc
69
+ extra_rdoc_files: []
70
+
43
71
  files:
44
72
  - .gitignore
45
- - Manifest
46
- - README.rdoc
73
+ - .rspec
74
+ - .travis.yml
75
+ - Gemfile
76
+ - README.md
47
77
  - Rakefile
48
- - VERSION
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
- - 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
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
- - --charset=UTF-8
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.3.4
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
- - 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
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
@@ -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