fear 0.9.0 → 1.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.
Files changed (155) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rubocop.yml +39 -0
  3. data/.github/workflows/spec.yml +42 -0
  4. data/.gitignore +0 -1
  5. data/.rubocop.yml +4 -12
  6. data/.simplecov +17 -0
  7. data/CHANGELOG.md +40 -0
  8. data/Gemfile +5 -2
  9. data/Gemfile.lock +130 -0
  10. data/LICENSE.txt +1 -1
  11. data/README.md +1293 -97
  12. data/Rakefile +369 -1
  13. data/benchmarks/README.md +1 -0
  14. data/benchmarks/dry_do_vs_fear_for.txt +11 -0
  15. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +11 -0
  16. data/benchmarks/factorial.txt +16 -0
  17. data/benchmarks/fear_gaurd_and1_vs_new.txt +13 -0
  18. data/benchmarks/fear_gaurd_and2_vs_and.txt +13 -0
  19. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +13 -0
  20. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +11 -0
  21. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +13 -0
  22. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +14 -0
  23. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +11 -0
  24. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +11 -0
  25. data/examples/pattern_extracting.rb +17 -0
  26. data/examples/pattern_extracting_ruby2.7.rb +15 -0
  27. data/examples/pattern_matching_binary_tree_set.rb +101 -0
  28. data/examples/pattern_matching_number_in_words.rb +60 -0
  29. data/fear.gemspec +34 -23
  30. data/lib/dry/types/fear.rb +8 -0
  31. data/lib/dry/types/fear/option.rb +125 -0
  32. data/lib/fear.rb +65 -15
  33. data/lib/fear/await.rb +33 -0
  34. data/lib/fear/awaitable.rb +28 -0
  35. data/lib/fear/either.rb +131 -71
  36. data/lib/fear/either_api.rb +23 -0
  37. data/lib/fear/either_pattern_match.rb +53 -0
  38. data/lib/fear/empty_partial_function.rb +38 -0
  39. data/lib/fear/extractor.rb +112 -0
  40. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +10 -0
  41. data/lib/fear/extractor/any_matcher.rb +17 -0
  42. data/lib/fear/extractor/array_head_matcher.rb +36 -0
  43. data/lib/fear/extractor/array_matcher.rb +40 -0
  44. data/lib/fear/extractor/array_splat_matcher.rb +16 -0
  45. data/lib/fear/extractor/empty_list_matcher.rb +20 -0
  46. data/lib/fear/extractor/extractor_matcher.rb +44 -0
  47. data/lib/fear/extractor/grammar.rb +203 -0
  48. data/lib/fear/extractor/grammar.treetop +129 -0
  49. data/lib/fear/extractor/identifier_matcher.rb +18 -0
  50. data/lib/fear/extractor/matcher.rb +53 -0
  51. data/lib/fear/extractor/matcher/and.rb +38 -0
  52. data/lib/fear/extractor/named_array_splat_matcher.rb +17 -0
  53. data/lib/fear/extractor/pattern.rb +58 -0
  54. data/lib/fear/extractor/typed_identifier_matcher.rb +26 -0
  55. data/lib/fear/extractor/value_matcher.rb +19 -0
  56. data/lib/fear/extractor_api.rb +35 -0
  57. data/lib/fear/failure.rb +46 -14
  58. data/lib/fear/failure_pattern_match.rb +10 -0
  59. data/lib/fear/for.rb +37 -95
  60. data/lib/fear/for_api.rb +68 -0
  61. data/lib/fear/future.rb +497 -0
  62. data/lib/fear/future_api.rb +21 -0
  63. data/lib/fear/left.rb +19 -2
  64. data/lib/fear/left_pattern_match.rb +11 -0
  65. data/lib/fear/none.rb +67 -3
  66. data/lib/fear/none_pattern_match.rb +14 -0
  67. data/lib/fear/option.rb +120 -56
  68. data/lib/fear/option_api.rb +40 -0
  69. data/lib/fear/option_pattern_match.rb +48 -0
  70. data/lib/fear/partial_function.rb +176 -0
  71. data/lib/fear/partial_function/and_then.rb +50 -0
  72. data/lib/fear/partial_function/any.rb +28 -0
  73. data/lib/fear/partial_function/combined.rb +53 -0
  74. data/lib/fear/partial_function/empty.rb +10 -0
  75. data/lib/fear/partial_function/guard.rb +80 -0
  76. data/lib/fear/partial_function/guard/and.rb +38 -0
  77. data/lib/fear/partial_function/guard/and3.rb +41 -0
  78. data/lib/fear/partial_function/guard/or.rb +38 -0
  79. data/lib/fear/partial_function/lifted.rb +23 -0
  80. data/lib/fear/partial_function/or_else.rb +64 -0
  81. data/lib/fear/partial_function_class.rb +38 -0
  82. data/lib/fear/pattern_match.rb +114 -0
  83. data/lib/fear/pattern_matching_api.rb +137 -0
  84. data/lib/fear/promise.rb +95 -0
  85. data/lib/fear/right.rb +20 -2
  86. data/lib/fear/right_biased.rb +6 -14
  87. data/lib/fear/right_pattern_match.rb +11 -0
  88. data/lib/fear/some.rb +55 -3
  89. data/lib/fear/some_pattern_match.rb +13 -0
  90. data/lib/fear/struct.rb +248 -0
  91. data/lib/fear/success.rb +35 -5
  92. data/lib/fear/success_pattern_match.rb +12 -0
  93. data/lib/fear/try.rb +136 -79
  94. data/lib/fear/try_api.rb +33 -0
  95. data/lib/fear/try_pattern_match.rb +33 -0
  96. data/lib/fear/unit.rb +32 -0
  97. data/lib/fear/utils.rb +39 -14
  98. data/lib/fear/version.rb +4 -1
  99. data/spec/dry/types/fear/option/constrained_spec.rb +22 -0
  100. data/spec/dry/types/fear/option/core_spec.rb +77 -0
  101. data/spec/dry/types/fear/option/default_spec.rb +21 -0
  102. data/spec/dry/types/fear/option/hash_spec.rb +58 -0
  103. data/spec/dry/types/fear/option/option_spec.rb +97 -0
  104. data/spec/fear/awaitable_spec.rb +17 -0
  105. data/spec/fear/done_spec.rb +8 -6
  106. data/spec/fear/either/mixin_spec.rb +17 -0
  107. data/spec/fear/either_pattern_match_spec.rb +37 -0
  108. data/spec/fear/either_pattern_matching_spec.rb +28 -0
  109. data/spec/fear/extractor/array_matcher_spec.rb +230 -0
  110. data/spec/fear/extractor/extractor_matcher_spec.rb +153 -0
  111. data/spec/fear/extractor/grammar_array_spec.rb +25 -0
  112. data/spec/fear/extractor/identified_matcher_spec.rb +49 -0
  113. data/spec/fear/extractor/identifier_matcher_spec.rb +68 -0
  114. data/spec/fear/extractor/pattern_spec.rb +34 -0
  115. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +64 -0
  116. data/spec/fear/extractor/value_matcher_number_spec.rb +79 -0
  117. data/spec/fear/extractor/value_matcher_string_spec.rb +88 -0
  118. data/spec/fear/extractor/value_matcher_symbol_spec.rb +71 -0
  119. data/spec/fear/extractor_api_spec.rb +115 -0
  120. data/spec/fear/extractor_spec.rb +61 -0
  121. data/spec/fear/failure_spec.rb +145 -45
  122. data/spec/fear/for_spec.rb +57 -67
  123. data/spec/fear/future_spec.rb +691 -0
  124. data/spec/fear/guard_spec.rb +103 -0
  125. data/spec/fear/left_spec.rb +112 -46
  126. data/spec/fear/none_spec.rb +114 -16
  127. data/spec/fear/option/mixin_spec.rb +39 -0
  128. data/spec/fear/option_pattern_match_spec.rb +35 -0
  129. data/spec/fear/option_pattern_matching_spec.rb +34 -0
  130. data/spec/fear/option_spec.rb +121 -8
  131. data/spec/fear/partial_function/empty_spec.rb +38 -0
  132. data/spec/fear/partial_function_and_then_spec.rb +147 -0
  133. data/spec/fear/partial_function_composition_spec.rb +82 -0
  134. data/spec/fear/partial_function_or_else_spec.rb +276 -0
  135. data/spec/fear/partial_function_spec.rb +239 -0
  136. data/spec/fear/pattern_match_spec.rb +93 -0
  137. data/spec/fear/pattern_matching_api_spec.rb +31 -0
  138. data/spec/fear/promise_spec.rb +96 -0
  139. data/spec/fear/right_biased/left.rb +29 -32
  140. data/spec/fear/right_biased/right.rb +51 -54
  141. data/spec/fear/right_spec.rb +109 -41
  142. data/spec/fear/some_spec.rb +80 -15
  143. data/spec/fear/success_spec.rb +99 -32
  144. data/spec/fear/try/mixin_spec.rb +19 -0
  145. data/spec/fear/try_pattern_match_spec.rb +37 -0
  146. data/spec/fear/try_pattern_matching_spec.rb +34 -0
  147. data/spec/fear/utils_spec.rb +16 -14
  148. data/spec/spec_helper.rb +13 -7
  149. data/spec/struct_pattern_matching_spec.rb +36 -0
  150. data/spec/struct_spec.rb +226 -0
  151. data/spec/support/dry_types.rb +6 -0
  152. metadata +320 -29
  153. data/.travis.yml +0 -9
  154. data/lib/fear/done.rb +0 -22
  155. data/lib/fear/for/evaluation_context.rb +0 -91
@@ -0,0 +1,276 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Fear::PartialFunction, "#or_else" do
4
+ subject(:first_or_else_second) { first.or_else(second) }
5
+
6
+ let(:first) { Fear.case(->(x) { x.even? }) { |x| "first: #{x}" } }
7
+ let(:second) { Fear.case(->(x) { x % 3 == 0 }) { |x| "second: #{x}" } }
8
+
9
+ describe "#defined_at?" do
10
+ context "first defined, second not" do
11
+ subject { first_or_else_second.defined_at?(4) }
12
+
13
+ it { is_expected.to eq(true) }
14
+ end
15
+
16
+ context "first not defined, second defined" do
17
+ subject { first_or_else_second.defined_at?(9) }
18
+
19
+ it { is_expected.to eq(true) }
20
+ end
21
+
22
+ context "first not defined, second not defined" do
23
+ subject { first_or_else_second.defined_at?(5) }
24
+
25
+ it { is_expected.to eq(false) }
26
+ end
27
+
28
+ context "first and second defined" do
29
+ subject { first_or_else_second.defined_at?(6) }
30
+
31
+ it { is_expected.to eq(true) }
32
+ end
33
+ end
34
+
35
+ describe "#call" do
36
+ context "first defined, second not" do
37
+ subject { first_or_else_second.(4) }
38
+
39
+ it { is_expected.to eq("first: 4") }
40
+ end
41
+
42
+ context "first not defined, second defined" do
43
+ subject { first_or_else_second.(9) }
44
+
45
+ it { is_expected.to eq("second: 9") }
46
+ end
47
+
48
+ context "first not defined, second not defined" do
49
+ subject { -> { first_or_else_second.(5) } }
50
+
51
+ it { is_expected.to raise_error(Fear::MatchError, "partial function not defined at: 5") }
52
+ end
53
+
54
+ context "first and second defined" do
55
+ subject { first_or_else_second.(6) }
56
+
57
+ it { is_expected.to eq("first: 6") }
58
+ end
59
+ end
60
+
61
+ describe "#call_or_else" do
62
+ let(:fallback) { ->(x) { "fallback: #{x}" } }
63
+
64
+ context "first defined, second not" do
65
+ subject { first_or_else_second.call_or_else(4, &fallback) }
66
+
67
+ it { is_expected.to eq("first: 4") }
68
+ end
69
+
70
+ context "first not defined, second defined" do
71
+ subject { first_or_else_second.call_or_else(9, &fallback) }
72
+
73
+ it { is_expected.to eq("second: 9") }
74
+ end
75
+
76
+ context "first not defined, second not defined" do
77
+ subject { first_or_else_second.call_or_else(5, &fallback) }
78
+
79
+ it { is_expected.to eq("fallback: 5") }
80
+ end
81
+
82
+ context "first and second defined" do
83
+ subject { first_or_else_second.call_or_else(6, &fallback) }
84
+
85
+ it { is_expected.to eq("first: 6") }
86
+ end
87
+ end
88
+
89
+ describe "#or_else" do
90
+ let(:first_or_else_second_or_else_third) { first_or_else_second.or_else(third) }
91
+ let(:third) { Fear.case(->(x) { x % 7 == 0 }) { |x| "third: #{x}" } }
92
+
93
+ describe "#defined_at?" do
94
+ context "first defined, second not" do
95
+ subject { first_or_else_second_or_else_third.defined_at?(4) }
96
+
97
+ it { is_expected.to eq(true) }
98
+ end
99
+
100
+ context "first not defined, second defined" do
101
+ subject { first_or_else_second_or_else_third.defined_at?(9) }
102
+
103
+ it { is_expected.to eq(true) }
104
+ end
105
+
106
+ context "first not defined, second not defined, third defined" do
107
+ subject { first_or_else_second_or_else_third.defined_at?(7) }
108
+
109
+ it { is_expected.to eq(true) }
110
+ end
111
+
112
+ context "first not defined, second not defined, third not defined" do
113
+ subject { first_or_else_second_or_else_third.defined_at?(1) }
114
+
115
+ it { is_expected.to eq(false) }
116
+ end
117
+
118
+ context "first, second and third defined" do
119
+ subject { first_or_else_second.defined_at?(42) }
120
+
121
+ it { is_expected.to eq(true) }
122
+ end
123
+ end
124
+
125
+ describe "#call" do
126
+ context "first defined, second not" do
127
+ subject { first_or_else_second_or_else_third.(4) }
128
+
129
+ it { is_expected.to eq("first: 4") }
130
+ end
131
+
132
+ context "first not defined, second defined" do
133
+ subject { first_or_else_second_or_else_third.(9) }
134
+
135
+ it { is_expected.to eq("second: 9") }
136
+ end
137
+
138
+ context "first not defined, second not defined, third defined" do
139
+ subject { first_or_else_second_or_else_third.(7) }
140
+
141
+ it { is_expected.to eq("third: 7") }
142
+ end
143
+
144
+ context "first not defined, second not defined, third not defined" do
145
+ subject { -> { first_or_else_second_or_else_third.(1) } }
146
+
147
+ it { is_expected.to raise_error(Fear::MatchError, "partial function not defined at: 1") }
148
+ end
149
+
150
+ context "first, second and third defined" do
151
+ subject { first_or_else_second.(42) }
152
+
153
+ it { is_expected.to eq("first: 42") }
154
+ end
155
+ end
156
+
157
+ describe "#call_or_else" do
158
+ let(:fallback) { ->(x) { "fallback: #{x}" } }
159
+
160
+ context "first defined, second not" do
161
+ subject { first_or_else_second_or_else_third.call_or_else(4, &fallback) }
162
+
163
+ it { is_expected.to eq("first: 4") }
164
+ end
165
+
166
+ context "first not defined, second defined" do
167
+ subject { first_or_else_second_or_else_third.call_or_else(9, &fallback) }
168
+
169
+ it { is_expected.to eq("second: 9") }
170
+ end
171
+
172
+ context "first not defined, second not defined, third defined" do
173
+ subject { first_or_else_second_or_else_third.call_or_else(7, &fallback) }
174
+
175
+ it { is_expected.to eq("third: 7") }
176
+ end
177
+
178
+ context "first not defined, second not defined, third not defined" do
179
+ subject { first_or_else_second_or_else_third.call_or_else(1, &fallback) }
180
+
181
+ it { is_expected.to eq("fallback: 1") }
182
+ end
183
+
184
+ context "first, second and third defined" do
185
+ subject { first_or_else_second_or_else_third.call_or_else(42, &fallback) }
186
+
187
+ it { is_expected.to eq("first: 42") }
188
+ end
189
+ end
190
+ end
191
+
192
+ describe "#and_then" do
193
+ let(:first_or_else_second_and_then_function) { first_or_else_second.and_then(&function) }
194
+ let(:function) { ->(x) { "f: #{x}" } }
195
+
196
+ describe "#defined_at?" do
197
+ context "first defined, second not" do
198
+ subject { first_or_else_second_and_then_function.defined_at?(2) }
199
+
200
+ it { is_expected.to eq(true) }
201
+ end
202
+
203
+ context "first not defined, second defined" do
204
+ subject { first_or_else_second_and_then_function.defined_at?(3) }
205
+
206
+ it { is_expected.to eq(true) }
207
+ end
208
+
209
+ context "first not defined, second not defined" do
210
+ subject { first_or_else_second_and_then_function.defined_at?(5) }
211
+
212
+ it { is_expected.to eq(false) }
213
+ end
214
+
215
+ context "first defined, second defined" do
216
+ subject { first_or_else_second_and_then_function.defined_at?(6) }
217
+
218
+ it { is_expected.to eq(true) }
219
+ end
220
+ end
221
+
222
+ describe "#call" do
223
+ context "first defined, second not" do
224
+ subject { first_or_else_second_and_then_function.(2) }
225
+
226
+ it { is_expected.to eq("f: first: 2") }
227
+ end
228
+
229
+ context "first not defined, second defined" do
230
+ subject { first_or_else_second_and_then_function.(3) }
231
+
232
+ it { is_expected.to eq("f: second: 3") }
233
+ end
234
+
235
+ context "first not defined, second not defined" do
236
+ subject { -> { first_or_else_second_and_then_function.(5) } }
237
+
238
+ it { is_expected.to raise_error(Fear::MatchError, "partial function not defined at: 5") }
239
+ end
240
+
241
+ context "first defined, second defined" do
242
+ subject { first_or_else_second_and_then_function.(6) }
243
+
244
+ it { is_expected.to eq("f: first: 6") }
245
+ end
246
+ end
247
+
248
+ describe "#call_or_else" do
249
+ let(:fallback) { ->(x) { "fallback: #{x}" } }
250
+
251
+ context "first defined, second not" do
252
+ subject { first_or_else_second_and_then_function.call_or_else(2, &fallback) }
253
+
254
+ it { is_expected.to eq("f: first: 2") }
255
+ end
256
+
257
+ context "first not defined, second defined" do
258
+ subject { first_or_else_second_and_then_function.call_or_else(3, &fallback) }
259
+
260
+ it { is_expected.to eq("f: second: 3") }
261
+ end
262
+
263
+ context "first not defined, second not defined" do
264
+ subject { first_or_else_second_and_then_function.call_or_else(5, &fallback) }
265
+
266
+ it { is_expected.to eq("fallback: 5") }
267
+ end
268
+
269
+ context "first defined, second defined" do
270
+ subject { first_or_else_second_and_then_function.call_or_else(6, &fallback) }
271
+
272
+ it { is_expected.to eq("f: first: 6") }
273
+ end
274
+ end
275
+ end
276
+ end
@@ -0,0 +1,239 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe Fear::PartialFunction do
4
+ describe "Fear.case()" do
5
+ context "condition is extractor" do
6
+ subject do
7
+ Fear.xcase("[1, [2, second_of_second, *], 3, *rest]") do |second_of_second:, rest:|
8
+ [second_of_second, rest]
9
+ end
10
+ end
11
+
12
+ it { is_expected.to be_defined_at([1, [2, 2, 3, 4], 3, 6, 7]) }
13
+ it { is_expected.not_to be_defined_at([1, [1, 3, 3, 4], 3, 6, 7]) }
14
+ it { is_expected.not_to be_defined_at([1, [1, 2, 3, 4], 4, 6, 7]) }
15
+ end
16
+
17
+ context "condition as symbol" do
18
+ subject { Fear.case(:even?) { |x| x } }
19
+
20
+ it "matches against the same symbol" do
21
+ is_expected.to be_defined_at(:even?)
22
+ is_expected.not_to be_defined_at(3)
23
+ end
24
+ end
25
+
26
+ context "condition as Class" do
27
+ subject { Fear.case(Integer) { |x| x } }
28
+
29
+ it do
30
+ is_expected.to be_defined_at(4)
31
+ is_expected.not_to be_defined_at("3")
32
+ end
33
+ end
34
+
35
+ context "condition as Proc" do
36
+ subject { Fear.case(->(x) { x.even? }) { |x| x } }
37
+
38
+ it do
39
+ is_expected.to be_defined_at(4)
40
+ is_expected.not_to be_defined_at(3)
41
+ end
42
+ end
43
+
44
+ context "multiple condition" do
45
+ subject { Fear.case(Integer, :even?.to_proc, ->(x) { x % 3 == 0 }) { |x| x } }
46
+
47
+ it do
48
+ is_expected.to be_defined_at(12)
49
+ is_expected.not_to be_defined_at(12.0)
50
+ is_expected.not_to be_defined_at("3")
51
+ is_expected.not_to be_defined_at(3)
52
+ is_expected.not_to be_defined_at(4)
53
+ end
54
+ end
55
+
56
+ context "multiple condition 2" do
57
+ subject { Fear.case(Integer, 4) { |x| x } }
58
+
59
+ it do
60
+ is_expected.to be_defined_at(4)
61
+ is_expected.not_to be_defined_at(3)
62
+ end
63
+ end
64
+ end
65
+
66
+ describe ".or" do
67
+ subject { described_class.or(guard_1, guard_2, &Fear::Utils::IDENTITY) }
68
+
69
+ let(:guard_1) { ->(x) { x == 42 } }
70
+ let(:guard_2) { ->(x) { x == 21 } }
71
+
72
+ it { is_expected.to be_defined_at(42) }
73
+ it { is_expected.to be_defined_at(21) }
74
+ it { is_expected.not_to be_defined_at(20) }
75
+ end
76
+
77
+ describe ".and" do
78
+ subject { described_class.and(guard_1, guard_2, &Fear::Utils::IDENTITY) }
79
+
80
+ let(:guard_1) { ->(x) { x % 5 == 0 } }
81
+ let(:guard_2) { ->(x) { x % 2 == 0 } }
82
+
83
+ it { is_expected.to be_defined_at(10) }
84
+ it { is_expected.not_to be_defined_at(5) }
85
+ it { is_expected.not_to be_defined_at(2) }
86
+ it { is_expected.not_to be_defined_at(3) }
87
+ end
88
+
89
+ describe "#lift" do
90
+ let(:lifted) { partial_function.lift }
91
+
92
+ let(:partial_function) { Fear.case(->(v) { v != 0 }) { |x| 4 / x } }
93
+
94
+ context "defined" do
95
+ subject { lifted.(2) }
96
+
97
+ it { is_expected.to eq(Fear::Some.new(2)) }
98
+ end
99
+
100
+ context "not defined" do
101
+ subject { lifted.(0) }
102
+
103
+ it { is_expected.to eq(Fear::None) }
104
+ end
105
+ end
106
+
107
+ describe "#defined_at?" do
108
+ let(:partial_function) { Fear.case(->(v) { v == 42 }) {} }
109
+
110
+ it "defined at" do
111
+ expect(partial_function.defined_at?(42)).to eq(true)
112
+ end
113
+
114
+ it "not defined at" do
115
+ expect(partial_function.defined_at?(24)).to eq(false)
116
+ end
117
+ end
118
+
119
+ describe "#call" do
120
+ let(:partial_function) { Fear.case(->(v) { v != 0 }) { |x| 4 / x } }
121
+
122
+ context "defined" do
123
+ subject { partial_function.(2) }
124
+
125
+ it { is_expected.to eq(2) }
126
+ end
127
+
128
+ context "not defined" do
129
+ subject { -> { partial_function.(0) } }
130
+
131
+ it { is_expected.to raise_error(Fear::MatchError, "partial function not defined at: 0") }
132
+ end
133
+
134
+ context "defined and condition is extractor" do
135
+ subject { partial_function.([1, 2, 3, 4, 5]) }
136
+
137
+ let(:partial_function) do
138
+ Fear.xcase("[1, second, 3, *rest]") { |second:, rest:| [second, rest] }
139
+ end
140
+
141
+ it { is_expected.to eq([2, [4, 5]]) }
142
+ end
143
+ end
144
+
145
+ describe "#to_proc", "#call" do
146
+ let(:partial_function) { Fear.case(->(v) { v != 0 }) { |x| 4 / x }.to_proc }
147
+
148
+ context "defined" do
149
+ subject { partial_function.(2) }
150
+
151
+ it { is_expected.to eq(2) }
152
+ end
153
+
154
+ context "not defined" do
155
+ subject { -> { partial_function.(0) } }
156
+
157
+ it { is_expected.to raise_error(Fear::MatchError, "partial function not defined at: 0") }
158
+ end
159
+ end
160
+
161
+ describe "#call_or_else" do
162
+ let(:default) { ->(x) { "division by #{x} impossible" } }
163
+ let(:partial_function) { Fear.case(->(x) { x != 0 }) { |x| 4 / x } }
164
+
165
+ context "defined" do
166
+ subject { partial_function.call_or_else(2, &default) }
167
+
168
+ it { is_expected.to eq(2) }
169
+ end
170
+
171
+ context "not defined" do
172
+ subject { partial_function.call_or_else(0, &default) }
173
+
174
+ it { is_expected.to eq("division by 0 impossible") }
175
+ end
176
+ end
177
+
178
+ describe "#and_then" do
179
+ let(:partial_function) { Fear.case(->(v) { v == 42 }) {} }
180
+ let(:and_then) { ->(x) { x } }
181
+
182
+ context "block given, arguments not given" do
183
+ subject { -> { partial_function.and_then(&and_then) } }
184
+
185
+ it { is_expected.not_to raise_error }
186
+ end
187
+
188
+ context "block given, argument given" do
189
+ subject { -> { partial_function.and_then(and_then, &and_then) } }
190
+
191
+ it { is_expected.to raise_error(ArgumentError) }
192
+ end
193
+
194
+ context "block given, arguments given" do
195
+ subject { -> { partial_function.and_then(and_then, 42, &and_then) } }
196
+
197
+ it { is_expected.to raise_error(ArgumentError) }
198
+ end
199
+
200
+ context "block not given, arguments not given" do
201
+ subject { -> { partial_function.and_then } }
202
+
203
+ it { is_expected.to raise_error(ArgumentError) }
204
+ end
205
+
206
+ context "block net given, arguments given" do
207
+ subject { -> { partial_function.and_then(and_then) } }
208
+
209
+ it { is_expected.not_to raise_error }
210
+ end
211
+ end
212
+
213
+ shared_examples "#or_else" do |method_name|
214
+ subject { is_even.__send__(method_name, is_odd).(value) }
215
+
216
+ let(:is_even) { Fear.case(:even?.to_proc) { |x| "#{x} is even" } }
217
+ let(:is_odd) { Fear.case(:odd?.to_proc) { |x| "#{x} is odd" } }
218
+
219
+ context "when left side is defined" do
220
+ let(:value) { 42 }
221
+
222
+ it { is_expected.to eq("42 is even") }
223
+ end
224
+
225
+ context "when left side is not defined" do
226
+ let(:value) { 21 }
227
+
228
+ it { is_expected.to eq("21 is odd") }
229
+ end
230
+ end
231
+
232
+ describe "#or_else" do
233
+ include_examples "#or_else", :or_else
234
+ end
235
+
236
+ describe "#|" do
237
+ include_examples "#or_else", :|
238
+ end
239
+ end