Skip to content

Commit 572c7d5

Browse files
committed
Implement laziness
1 parent 6adacaf commit 572c7d5

File tree

2 files changed

+104
-5
lines changed

2 files changed

+104
-5
lines changed

lib/possibly.rb

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ class Maybe
22
([:each] + Enumerable.instance_methods).each do |enumerable_method|
33
define_method(enumerable_method) do |*args, &block|
44
res = __enumerable_value.send(enumerable_method, *args, &block)
5-
res.respond_to?(:each) ? Maybe(res.first) : res
5+
res.respond_to?(:each) ? rewrap(res) : res
66
end
77
end
88

9+
def initialize(lazy_enumerable)
10+
@lazy = lazy_enumerable
11+
end
12+
913
def to_ary
1014
__enumerable_value
1115
end
@@ -15,6 +19,53 @@ def ==(other)
1519
other.class == self.class
1620
end
1721
alias_method :eql?, :==
22+
23+
def get
24+
__evaluated.get
25+
end
26+
27+
def or_else(*args)
28+
__evaluated.or_else(*args)
29+
end
30+
31+
# rubocop:disable PredicateName
32+
def is_some?
33+
__evaluated.is_some?
34+
end
35+
36+
def is_none?
37+
__evaluated.is_none?
38+
end
39+
# rubocop:enable PredicateName
40+
41+
def lazy
42+
Maybe.new(__enumerable_value.lazy)
43+
end
44+
45+
private
46+
47+
def __enumerable_value
48+
@lazy
49+
end
50+
51+
def __evaluated
52+
@evaluated ||= Maybe(@lazy.first)
53+
end
54+
55+
def rewrap(enumerable)
56+
Maybe.new(enumerable)
57+
end
58+
59+
def self.from_block(&block)
60+
Maybe.new(lazy_enum_from_block(&block))
61+
end
62+
63+
def self.lazy_enum_from_block(&block)
64+
Enumerator.new do |yielder|
65+
yielder << block.call
66+
end.lazy
67+
end
68+
1869
end
1970

2071
# Represents a non-empty value
@@ -59,10 +110,16 @@ def method_missing(method_sym, *args, &block)
59110
def __enumerable_value
60111
[@value]
61112
end
113+
114+
def rewrap(enumerable)
115+
Maybe(enumerable.first)
116+
end
62117
end
63118

64119
# Represents an empty value
65120
class None < Maybe
121+
def initialize; end
122+
66123
def get
67124
fail 'No such element'
68125
end
@@ -93,11 +150,15 @@ def __enumerable_value
93150
end
94151

95152
# rubocop:disable MethodName
96-
def Maybe(value)
97-
if value.nil? || (value.respond_to?(:length) && value.length == 0)
98-
None()
153+
def Maybe(value = nil, &block)
154+
if block
155+
Maybe.from_block(&block)
99156
else
100-
Some(value)
157+
if value.nil? || (value.respond_to?(:length) && value.length == 0)
158+
None()
159+
else
160+
Some(value)
161+
end
101162
end
102163
end
103164

spec/spec.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,42 @@ def test_case_when(case_value, match_value, non_match_value)
156156
expect(Some([1, 2, 3]).map { |arr| arr.map { |v| v * v } }.get).to eql([1, 4, 9])
157157
end
158158
end
159+
160+
describe "laziness" do
161+
it "can be initialized lazily" do
162+
init_called = false
163+
map_called = false
164+
165+
m = Maybe do
166+
init_called = true
167+
2
168+
end.map do |v|
169+
map_called = true
170+
v * v
171+
end
172+
173+
expect(init_called).to eql(false)
174+
expect(map_called).to eql(false)
175+
expect(m.get).to eql(4)
176+
expect(init_called).to eql(true)
177+
expect(map_called).to eql(true)
178+
end
179+
180+
it "can be converted to lazy" do
181+
map_called = false
182+
183+
m = Maybe(2).lazy.map do |v|
184+
map_called = true
185+
v * v
186+
end
187+
188+
expect(map_called).to eql(false)
189+
expect(m.get).to eql(4)
190+
expect(map_called).to eql(true)
191+
end
192+
end
193+
194+
def factors(num)
195+
Maybe((2..num - 1).select { |n| num % n == 0 })
196+
end
159197
end

0 commit comments

Comments
 (0)