call_by_need 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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