call_by_need 0.1.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/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,26 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ call_by_need (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.1.3)
10
+ rake (10.0.4)
11
+ rspec (2.5.0)
12
+ rspec-core (~> 2.5.0)
13
+ rspec-expectations (~> 2.5.0)
14
+ rspec-mocks (~> 2.5.0)
15
+ rspec-core (2.5.2)
16
+ rspec-expectations (2.5.0)
17
+ diff-lcs (~> 1.1.2)
18
+ rspec-mocks (2.5.0)
19
+
20
+ PLATFORMS
21
+ ruby
22
+
23
+ DEPENDENCIES
24
+ call_by_need!
25
+ rake
26
+ rspec
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new
7
+
8
+ task :default => :spec
9
+ task :test => :spec
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/call_by_need/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["fronx"]
6
+ gem.email = ["fronx@wurmus.de"]
7
+ gem.description = %q{A little call-by-need implementation for recreational use.}
8
+ gem.summary = %q{A scoped call-by-need module/class.}
9
+ gem.homepage = "https://github.com/fronx/call_by_need.rb"
10
+
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
13
+ gem.name = "call_by_need"
14
+ gem.require_paths = ["lib"]
15
+ gem.version = CallByNeed::VERSION
16
+
17
+ gem.add_development_dependency 'rake'
18
+ gem.add_development_dependency 'rspec'
19
+ end
@@ -0,0 +1,63 @@
1
+ module CallByNeed
2
+ class VarMissingError < StandardError
3
+ def initialize(name, context)
4
+ super("`#{name}` was expected, but doesn't exist in context `#{context.inspect}`.")
5
+ end
6
+ end
7
+
8
+ class DuplicateVarError < StandardError
9
+ def initialize(name, context)
10
+ super("`#{name}` can't be initialized twice in context `#{context.inspect}`.")
11
+ end
12
+ end
13
+
14
+ def declare(name, &block)
15
+ var_set(name, block)
16
+ class << self; self; end.class_eval do
17
+ define_method(name) do
18
+ get(name)
19
+ end
20
+ end
21
+ end
22
+
23
+ def declared?(name)
24
+ store.has_key?(name)
25
+ end
26
+
27
+ def get(name)
28
+ if var_get(name).respond_to?(:call)
29
+ store[name] = var_get(name).call(self)
30
+ else
31
+ var_get(name)
32
+ end
33
+ end
34
+
35
+ def keys
36
+ store.keys
37
+ end
38
+
39
+ def [](key)
40
+ var_get(key)
41
+ end
42
+
43
+ private
44
+ def var_get(name)
45
+ if store.has_key?(name)
46
+ store[name]
47
+ else
48
+ raise VarMissingError.new(name, self)
49
+ end
50
+ end
51
+
52
+ def var_set(name, value)
53
+ if store.has_key?(name) && (store[name] != value)
54
+ raise DuplicateVarError.new(name, self)
55
+ else
56
+ store[name] = value # returns value
57
+ end
58
+ end
59
+
60
+ def store
61
+ @__store ||= {}
62
+ end
63
+ end
@@ -0,0 +1,22 @@
1
+ module CallByNeed
2
+ class Context
3
+ include CallByNeed
4
+
5
+ def initialize(*others, &block)
6
+ others.each do |other|
7
+ other.keys.each do |k|
8
+ if other[k].respond_to?(:call)
9
+ declare(k, &other[k])
10
+ else # item has already been evaluated
11
+ declare(k) { other[k] }
12
+ end
13
+ end
14
+ end
15
+ block.call(self) if block
16
+ end
17
+
18
+ def merge(*others)
19
+ self.class.new(self, *others)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,3 @@
1
+ module CallByNeed
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,3 @@
1
+ require "call_by_need/version"
2
+ require 'call_by_need/call_by_need'
3
+ require 'call_by_need/context'
data/readme.md ADDED
@@ -0,0 +1,3 @@
1
+ # Call By Need
2
+
3
+ So yeah, uhm, you can read about what that means on [Wikipedia](http://en.wikipedia.org/wiki/Evaluation_strategy#Call_by_need). It's like an on-line encyclopedia type thing.
@@ -0,0 +1,101 @@
1
+ require 'bundler/setup'
2
+ require 'call_by_need'
3
+
4
+ class CallByNeedTest
5
+ include CallByNeed
6
+ attr_reader :a
7
+ def initialize(a)
8
+ @a = a
9
+ end
10
+ end
11
+
12
+ describe CallByNeed do
13
+ let(:object) do
14
+ CallByNeedTest.new(123)
15
+ end
16
+
17
+ it "executes in the calling scope" do
18
+ @a = nil
19
+ object.a.should == 123
20
+ object.declare(:foo) { @a = 3 * 9 }
21
+ object.foo.should == 27
22
+ @a.should == 27
23
+ end
24
+ end
25
+
26
+ describe CallByNeed::Context do
27
+ def something
28
+ @x ||= "something"
29
+ end
30
+
31
+ let(:context) do
32
+ CallByNeed::Context.new do |c|
33
+ c.declare(:a) { something }
34
+ c.declare(:b) { something + something }
35
+ end
36
+ end
37
+
38
+ it "disallows overwriting values" do
39
+ lambda do
40
+ context.declare(:a) { "boo" }
41
+ end.should raise_error(CallByNeed::DuplicateVarError)
42
+ end
43
+
44
+ it "throws an error when reading nonexisting values" do
45
+ lambda do
46
+ context.get(:x)
47
+ end.should raise_error(CallByNeed::VarMissingError)
48
+ end
49
+
50
+ it "can read values repeatedly" do
51
+ context.a.should == 'something'
52
+ context.a.should == 'something'
53
+ end
54
+
55
+ it "executes the block for a name once" do
56
+ context.a.should == 'something' # cached hereafter
57
+ @x = 'else'
58
+ context.a.should == 'something' # memoized
59
+ context.b.should == 'elseelse' # evaluated later --> different value
60
+ end
61
+
62
+ it "allows self-references" do
63
+ context = CallByNeed::Context.new do |c|
64
+ c.declare(:x) { 2 * c.y }
65
+ c.declare(:z) { 3 * c.x }
66
+ c.declare(:y) { 4 }
67
+ end
68
+ context.z.should == 24
69
+ end
70
+
71
+ it "can be merged" do
72
+ c1 = CallByNeed::Context.new { |c| c.declare(:a) { 1 } }
73
+ c2 = CallByNeed::Context.new { |c| c.declare(:b) { 2 } }
74
+ c3 = c1.merge(c2)
75
+ c3.a.should == 1
76
+ c3.b.should == 2
77
+ end
78
+
79
+ it "can inherit" do
80
+ c1 = CallByNeed::Context.new { |c| c.declare(:a) { 2 } }
81
+ c2 = CallByNeed::Context.new(c1) { |c| c.declare(:b) { c.a * 4 } }
82
+ c2.a.should == 2
83
+ c2.b.should == 8
84
+ end
85
+
86
+ it "doesn't care what the order of dependent declarations is" do
87
+ CallByNeed::Context.new do |c|
88
+ c.declare(:a, &:b)
89
+ c.declare(:b) { 'b' }
90
+ end.a.should == 'b'
91
+ c1 = CallByNeed::Context.new do |c|
92
+ c.declare(:a, &:b)
93
+ c.declare(:x) { |x| x.b + 'x' }
94
+ end
95
+ c2 = CallByNeed::Context.new do |c|
96
+ c.declare(:b) { 'b' }
97
+ end
98
+ c2.merge(c1).a.should == 'b'
99
+ c2.merge(c1).x.should == 'bx'
100
+ end
101
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: call_by_need
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.1.0
6
+ platform: ruby
7
+ authors:
8
+ - fronx
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2013-09-07 00:00:00 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rake
17
+ requirement: &id001 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ type: :development
24
+ prerelease: false
25
+ version_requirements: *id001
26
+ - !ruby/object:Gem::Dependency
27
+ name: rspec
28
+ requirement: &id002 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: *id002
37
+ description: A little call-by-need implementation for recreational use.
38
+ email:
39
+ - fronx@wurmus.de
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files: []
45
+
46
+ files:
47
+ - Gemfile
48
+ - Gemfile.lock
49
+ - Rakefile
50
+ - call_by_need.gemspec
51
+ - lib/call_by_need.rb
52
+ - lib/call_by_need/call_by_need.rb
53
+ - lib/call_by_need/context.rb
54
+ - lib/call_by_need/version.rb
55
+ - readme.md
56
+ - spec/call_by_need_spec.rb
57
+ homepage: https://github.com/fronx/call_by_need.rb
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options: []
62
+
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 1747255289361994175
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ hash: 1747255289361994175
80
+ segments:
81
+ - 0
82
+ version: "0"
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.8.24
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: A scoped call-by-need module/class.
90
+ test_files:
91
+ - spec/call_by_need_spec.rb