deterministic 0.15.3 → 0.16.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.
- checksums.yaml +4 -4
- data/README.md +36 -30
- data/lib/deterministic/enum.rb +34 -14
- data/lib/deterministic/match.rb +1 -0
- data/lib/deterministic/option.rb +9 -9
- data/lib/deterministic/result.rb +12 -12
- data/lib/deterministic/version.rb +1 -1
- data/spec/examples/amount_spec.rb +6 -6
- data/spec/examples/config_spec.rb +3 -4
- data/spec/examples/list.rb +37 -37
- data/spec/examples/list_spec.rb +7 -7
- data/spec/examples/logger_spec.rb +2 -0
- data/spec/lib/deterministic/option_spec.rb +4 -4
- data/spec/lib/enum_spec.rb +9 -9
- data/spec/readme_spec.rb +6 -7
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7120823c22300ba01fd44a7a3d18aae96f7357ba
|
4
|
+
data.tar.gz: 8cd374ad1eed4bba6aae559d96730954d76b15bd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a15bd39adfa6dd90d2a9f5f395aba58881bab0380b743770a5dc6d09bf87d4681b7ae7493f344a66ae771a4f084e898d853c3f9745c9b165df27d9a7b928c49
|
7
|
+
data.tar.gz: d0df2e74a8a941216a1a58b836916dd55e5ed15d90c3e93dcd22d611e20a3b041dbf7ed4c767ad47c08c38eab7e2ccc60c76c10a9290c7125bb32c5321a1dd71
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Deterministic is to help your code to be more confident, by utilizing functional programming patterns.
|
7
7
|
|
8
|
-
This is a spiritual successor of the [Monadic gem](http://github.com/pzol/monadic). The goal of the rewrite is to get away from a bit
|
8
|
+
This is a spiritual successor of the [Monadic gem](http://github.com/pzol/monadic). The goal of the rewrite is to get away from a bit too forceful aproach I took in Monadic, especially when it comes to coercing monads, but also a more practical but at the same time more strict adherence to monad laws.
|
9
9
|
|
10
10
|
## Patterns
|
11
11
|
|
@@ -213,29 +213,35 @@ Now that you have some result, you want to control flow by providing patterns.
|
|
213
213
|
|
214
214
|
```ruby
|
215
215
|
Success(1).match do
|
216
|
-
Success(
|
217
|
-
Failure(
|
216
|
+
Success() { |s| "success #{s}"}
|
217
|
+
Failure() { |f| "failure #{f}"}
|
218
218
|
end # => "success 1"
|
219
219
|
```
|
220
|
-
Note1: the inner value
|
220
|
+
Note1: the variant's inner value(s) have been unwrapped, and passed to the block.
|
221
221
|
|
222
222
|
Note2: only the __first__ matching pattern block will be executed, so order __can__ be important.
|
223
223
|
|
224
|
+
Note3: you can omit block parameters if you don't use them, or you can use `_` to signify that you don't care about their values. If you specify parameters, their number must match the number of values in the variant.
|
225
|
+
|
224
226
|
The result returned will be the result of the __first__ `#try` or `#let`. As a side note, `#try` is a monad, `#let` is a functor.
|
225
227
|
|
226
228
|
Guards
|
227
229
|
|
228
230
|
```ruby
|
229
231
|
Success(1).match do
|
230
|
-
Success(
|
232
|
+
Success(where { s == 1 }) { |s| "Success #{s}" }
|
231
233
|
end # => "Success 1"
|
232
234
|
```
|
233
235
|
|
236
|
+
Note1: the guard has access to variable names defined by the block arguments.
|
237
|
+
|
238
|
+
Note2: the guard is not evaluated using the enclosing context's `self`; if you need to call methods on the enclosing scope, you must specify a receiver.
|
239
|
+
|
234
240
|
Also you can match the result class
|
235
241
|
|
236
242
|
```ruby
|
237
243
|
Success([1, 2, 3]).match do
|
238
|
-
Success(
|
244
|
+
Success(where { s.is_a?(Array) }) { |s| s.first }
|
239
245
|
end # => 1
|
240
246
|
```
|
241
247
|
|
@@ -243,7 +249,7 @@ If no match was found a `NoMatchError` is raised, so make sure you always cover
|
|
243
249
|
|
244
250
|
```ruby
|
245
251
|
Success(1).match do
|
246
|
-
Failure(
|
252
|
+
Failure() { |f| "you'll never get me" }
|
247
253
|
end # => NoMatchError
|
248
254
|
```
|
249
255
|
|
@@ -323,9 +329,9 @@ Option.try! { raise "error"} # => None
|
|
323
329
|
### Pattern Matching
|
324
330
|
```ruby
|
325
331
|
Some(1).match {
|
326
|
-
Some(
|
327
|
-
Some(
|
328
|
-
None()
|
332
|
+
Some(where { s == 1 }) { |s| s + 1 }
|
333
|
+
Some() { |s| 1 }
|
334
|
+
None() { 0 }
|
329
335
|
} # => 2
|
330
336
|
```
|
331
337
|
|
@@ -361,28 +367,28 @@ Pattern matching
|
|
361
367
|
|
362
368
|
```ruby
|
363
369
|
Threenum::Unary(5).match {
|
364
|
-
Nullary()
|
365
|
-
Unary(
|
366
|
-
Binary(a, b
|
370
|
+
Nullary() { 0 }
|
371
|
+
Unary() { |u| u }
|
372
|
+
Binary() { |a, b| a + b }
|
367
373
|
} # => 5
|
368
374
|
|
369
375
|
# or
|
370
376
|
t = Threenum::Unary(5)
|
371
377
|
Threenum.match(t) {
|
372
|
-
Nullary()
|
373
|
-
Unary(
|
374
|
-
Binary(a, b
|
378
|
+
Nullary() { 0 }
|
379
|
+
Unary() { |u| u }
|
380
|
+
Binary() { |a, b| a + b }
|
375
381
|
} # => 5
|
376
382
|
```
|
377
383
|
|
378
|
-
If you want the whole
|
384
|
+
If you want to return the whole matched object, you'll need to pass a reference to the object (second case). Note that `self` refers to the scope enclosing the `match` call.
|
379
385
|
|
380
386
|
```ruby
|
381
387
|
def drop(n)
|
382
388
|
match {
|
383
|
-
Cons(
|
384
|
-
Cons(_, _
|
385
|
-
Nil() { raise EmptyListError}
|
389
|
+
Cons(where { n > 0 }) { |h, t| t.drop(n - 1) }
|
390
|
+
Cons() { |_, _| self }
|
391
|
+
Nil() { raise EmptyListError }
|
386
392
|
}
|
387
393
|
end
|
388
394
|
```
|
@@ -393,10 +399,10 @@ With guard clauses
|
|
393
399
|
|
394
400
|
```ruby
|
395
401
|
Threenum::Unary(5).match {
|
396
|
-
Nullary()
|
397
|
-
Unary(
|
398
|
-
Binary(
|
399
|
-
Binary(a, b
|
402
|
+
Nullary() { 0 }
|
403
|
+
Unary() { |u| u }
|
404
|
+
Binary(where { a.is_a?(Fixnum) && b.is_a?(Fixnum) }) { |a, b| a + b }
|
405
|
+
Binary() { |a, b| raise "Expected a, b to be numbers" }
|
400
406
|
} # => 5
|
401
407
|
```
|
402
408
|
|
@@ -406,17 +412,17 @@ Implementing methods for enums
|
|
406
412
|
Deterministic::impl(Threenum) {
|
407
413
|
def sum
|
408
414
|
match {
|
409
|
-
Nullary()
|
410
|
-
Unary(
|
411
|
-
Binary(a, b
|
415
|
+
Nullary() { 0 }
|
416
|
+
Unary() { |u| u }
|
417
|
+
Binary() { |a, b| a + b }
|
412
418
|
}
|
413
419
|
end
|
414
420
|
|
415
421
|
def +(other)
|
416
422
|
match {
|
417
|
-
Nullary()
|
418
|
-
Unary(
|
419
|
-
Binary(a, b
|
423
|
+
Nullary() { other.sum }
|
424
|
+
Unary() { |a| self.sum + other.sum }
|
425
|
+
Binary() { |a, b| self.sum + other.sum }
|
420
426
|
}
|
421
427
|
end
|
422
428
|
}
|
data/lib/deterministic/enum.rb
CHANGED
@@ -23,6 +23,16 @@ module Deterministic
|
|
23
23
|
def name
|
24
24
|
self.class.name.split("::")[-1]
|
25
25
|
end
|
26
|
+
|
27
|
+
# Returns array. Will fail on Nullary objects.
|
28
|
+
# TODO: define a Unary module so we can define this method differently on Unary vs Binary
|
29
|
+
def wrapped_values
|
30
|
+
if self.is_a?(Deterministic::EnumBuilder::DataType::Binary)
|
31
|
+
value.values
|
32
|
+
else
|
33
|
+
[value]
|
34
|
+
end
|
35
|
+
end
|
26
36
|
end
|
27
37
|
|
28
38
|
module Nullary
|
@@ -35,6 +45,7 @@ module Deterministic
|
|
35
45
|
end
|
36
46
|
end
|
37
47
|
|
48
|
+
# TODO: this should probably be named Multary
|
38
49
|
module Binary
|
39
50
|
def initialize(*init)
|
40
51
|
raise ArgumentError, "Expected arguments for #{args}, got #{init}" unless (init.count == 1 && init[0].is_a?(Hash)) || init.count == args.count
|
@@ -103,6 +114,8 @@ module_function
|
|
103
114
|
class << self; private :new; end
|
104
115
|
|
105
116
|
def self.match(obj, &block)
|
117
|
+
caller_ctx = block.binding.eval 'self'
|
118
|
+
|
106
119
|
matcher = self::Matcher.new(obj)
|
107
120
|
matcher.instance_eval(&block)
|
108
121
|
|
@@ -116,17 +129,19 @@ module_function
|
|
116
129
|
obj, type, block, args, guard = match
|
117
130
|
|
118
131
|
if args.count == 0
|
119
|
-
return
|
132
|
+
return caller_ctx.instance_eval(&block)
|
120
133
|
else
|
121
|
-
|
122
|
-
|
134
|
+
if args.count != obj.args.count
|
135
|
+
raise Enum::MatchError, "Pattern (#{args.join(', ')}) must match (#{obj.args.join(', ')})"
|
136
|
+
end
|
137
|
+
guard_ctx = guard_context(obj, args)
|
123
138
|
|
124
139
|
if guard
|
125
|
-
if
|
126
|
-
return
|
140
|
+
if guard_ctx.instance_exec(obj, &guard)
|
141
|
+
return caller_ctx.instance_exec(* obj.wrapped_values, &block)
|
127
142
|
end
|
128
143
|
else
|
129
|
-
return
|
144
|
+
return caller_ctx.instance_exec(* obj.wrapped_values, &block)
|
130
145
|
end
|
131
146
|
end
|
132
147
|
}
|
@@ -137,7 +152,7 @@ module_function
|
|
137
152
|
def self.variants; constants - [:Matcher, :MatchError]; end
|
138
153
|
|
139
154
|
private
|
140
|
-
def self.
|
155
|
+
def self.guard_context(obj, args)
|
141
156
|
if obj.is_a?(Deterministic::EnumBuilder::DataType::Binary)
|
142
157
|
Struct.new(*(args)).new(*(obj.value.values))
|
143
158
|
else
|
@@ -163,17 +178,22 @@ module_function
|
|
163
178
|
guard
|
164
179
|
end
|
165
180
|
|
166
|
-
def method_missing(m)
|
167
|
-
m
|
168
|
-
end
|
169
|
-
|
170
181
|
type_variants.each { |m|
|
171
|
-
define_method(m) {
|
182
|
+
define_method(m) { |guard = nil, &block|
|
172
183
|
raise ArgumentError, "No block given to `#{m}`" if block.nil?
|
184
|
+
params_spec = block.parameters
|
185
|
+
if params_spec.any? {|spec| spec.size < 2 }
|
186
|
+
raise ArgumentError, "Unnamed param found in block parameters: #{params_spec.inspect}"
|
187
|
+
end
|
188
|
+
if params_spec.any? {|spec| spec[0] != :req && spec[0] != :opt }
|
189
|
+
raise ArgumentError, "Only :req & :opt params allowed; parameters=#{params_spec.inspect}"
|
190
|
+
end
|
191
|
+
args = params_spec.map {|spec| spec[1] }
|
192
|
+
|
173
193
|
type = Kernel.eval("#{mod.name}::#{m}")
|
174
194
|
|
175
|
-
if
|
176
|
-
guard =
|
195
|
+
if guard && !guard.is_a?(Proc)
|
196
|
+
guard = nil
|
177
197
|
end
|
178
198
|
|
179
199
|
@matches << [@obj, type, block, args, guard]
|
data/lib/deterministic/match.rb
CHANGED
data/lib/deterministic/option.rb
CHANGED
@@ -29,15 +29,15 @@ module Deterministic
|
|
29
29
|
|
30
30
|
def fmap(&fn)
|
31
31
|
match {
|
32
|
-
Some(
|
33
|
-
None()
|
32
|
+
Some() {|s| self.class.new(fn.(s)) }
|
33
|
+
None() { self }
|
34
34
|
}
|
35
35
|
end
|
36
36
|
|
37
37
|
def map(&fn)
|
38
38
|
match {
|
39
|
-
Some(
|
40
|
-
None()
|
39
|
+
Some() {|s| self.bind(&fn) }
|
40
|
+
None() { self }
|
41
41
|
}
|
42
42
|
end
|
43
43
|
|
@@ -53,8 +53,8 @@ module Deterministic
|
|
53
53
|
|
54
54
|
def value_or(n)
|
55
55
|
match {
|
56
|
-
Some(
|
57
|
-
None()
|
56
|
+
Some() {|s| s }
|
57
|
+
None() { n }
|
58
58
|
}
|
59
59
|
end
|
60
60
|
|
@@ -65,9 +65,9 @@ module Deterministic
|
|
65
65
|
def +(other)
|
66
66
|
match {
|
67
67
|
None() { other }
|
68
|
-
Some(
|
69
|
-
Some(
|
70
|
-
Some(
|
68
|
+
Some(where { !other.is_a?(Option) }) {|_| raise TypeError, "Other must be an #{Option}"}
|
69
|
+
Some(where { other.some? }) {|s| Option::Some.new(s + other.value) }
|
70
|
+
Some() {|_| self }
|
71
71
|
}
|
72
72
|
end
|
73
73
|
}
|
data/lib/deterministic/result.rb
CHANGED
@@ -19,8 +19,8 @@ module Deterministic
|
|
19
19
|
Deterministic::impl(Result) {
|
20
20
|
def map(proc=nil, &block)
|
21
21
|
match {
|
22
|
-
Success(
|
23
|
-
Failure(
|
22
|
+
Success() {|_| self.bind(proc || block) }
|
23
|
+
Failure() {|_| self }
|
24
24
|
}
|
25
25
|
end
|
26
26
|
|
@@ -29,8 +29,8 @@ module Deterministic
|
|
29
29
|
|
30
30
|
def map_err(proc=nil, &block)
|
31
31
|
match {
|
32
|
-
Success(
|
33
|
-
Failure(
|
32
|
+
Success() {|_| self }
|
33
|
+
Failure() {|_| self.bind(proc|| block) }
|
34
34
|
}
|
35
35
|
end
|
36
36
|
|
@@ -54,26 +54,26 @@ module Deterministic
|
|
54
54
|
def or(other)
|
55
55
|
raise Deterministic::Monad::NotMonadError, "Expected #{other.inspect} to be a Result" unless other.is_a? Result
|
56
56
|
match {
|
57
|
-
Success(
|
58
|
-
Failure(
|
57
|
+
Success() {|_| self }
|
58
|
+
Failure() {|_| other }
|
59
59
|
}
|
60
60
|
end
|
61
61
|
|
62
62
|
def and(other)
|
63
63
|
raise Deterministic::Monad::NotMonadError, "Expected #{other.inspect} to be a Result" unless other.is_a? Result
|
64
64
|
match {
|
65
|
-
Success(
|
66
|
-
Failure(
|
65
|
+
Success() {|_| other }
|
66
|
+
Failure() {|_| self }
|
67
67
|
}
|
68
68
|
end
|
69
69
|
|
70
70
|
def +(other)
|
71
71
|
raise Deterministic::Monad::NotMonadError, "Expected #{other.inspect} to be a Result" unless other.is_a? Result
|
72
72
|
match {
|
73
|
-
Success(
|
74
|
-
Failure(
|
75
|
-
Success(
|
76
|
-
Failure(
|
73
|
+
Success(where { other.success? } ) {|s| Result::Success.new(s + other.value) }
|
74
|
+
Failure(where { other.failure? } ) {|f| Result::Failure.new(f + other.value) }
|
75
|
+
Success() {|_| other } # implied other.failure?
|
76
|
+
Failure() {|_| self } # implied other.success?
|
77
77
|
}
|
78
78
|
end
|
79
79
|
|
@@ -16,17 +16,17 @@ end
|
|
16
16
|
Deterministic::impl(Amount) {
|
17
17
|
def to_s
|
18
18
|
match {
|
19
|
-
Due(
|
20
|
-
Paid(
|
21
|
-
Info(
|
19
|
+
Due() {|a| "%0.2f" % [a] }
|
20
|
+
Paid() {|a| "-%0.2f" % [a] }
|
21
|
+
Info() {|a| "(%0.2f)" % [a] }
|
22
22
|
}
|
23
23
|
end
|
24
24
|
|
25
25
|
def to_f
|
26
26
|
match {
|
27
|
-
Info(
|
28
|
-
Due(
|
29
|
-
Paid(
|
27
|
+
Info() {|a| 0 }
|
28
|
+
Due() {|a| a }
|
29
|
+
Paid() {|a| -1 * a }
|
30
30
|
}
|
31
31
|
end
|
32
32
|
|
@@ -11,8 +11,8 @@ class ElasticSearchConfig
|
|
11
11
|
|
12
12
|
def hosts
|
13
13
|
Option.any?(proc_env["RESFINITY_LOG_CLIENT_ES_HOST"]).match {
|
14
|
-
Some(
|
15
|
-
None() { default_hosts }
|
14
|
+
Some() {|s| { hosts: s.split(/, */) } }
|
15
|
+
None() { default_hosts } # calls ElasticSearchConfig instance's method
|
16
16
|
}
|
17
17
|
end
|
18
18
|
|
@@ -31,7 +31,7 @@ private
|
|
31
31
|
end
|
32
32
|
|
33
33
|
describe ElasticSearchConfig do
|
34
|
-
|
34
|
+
# NOTE: the "empty" cases also verify that the variant matchers use the enclosing context as self
|
35
35
|
|
36
36
|
let(:cfg) { ElasticSearchConfig.new(environment, env) }
|
37
37
|
context "test" do
|
@@ -72,5 +72,4 @@ describe ElasticSearchConfig do
|
|
72
72
|
specify { expect(cfg.hosts).to eq({ hosts: ["acc.resfinity.net:9200"] }) }
|
73
73
|
end
|
74
74
|
end
|
75
|
-
}
|
76
75
|
end
|
data/spec/examples/list.rb
CHANGED
@@ -28,46 +28,46 @@ Deterministic::impl(List) {
|
|
28
28
|
|
29
29
|
def first
|
30
30
|
match {
|
31
|
-
Cons(_, _
|
32
|
-
Nil() {
|
31
|
+
Cons() {|_, _| self }
|
32
|
+
Nil() { self }
|
33
33
|
}
|
34
34
|
end
|
35
35
|
|
36
36
|
def last
|
37
37
|
match {
|
38
|
-
Cons(
|
39
|
-
Cons(_, t
|
40
|
-
Nil() {
|
38
|
+
Cons(where { t.null? }) {|h, t| return self }
|
39
|
+
Cons() {|_, t| t.last }
|
40
|
+
Nil() { self }
|
41
41
|
}
|
42
42
|
end
|
43
43
|
|
44
44
|
def head
|
45
45
|
match {
|
46
|
-
Cons(h, _
|
47
|
-
Nil() {
|
46
|
+
Cons() {|h, _| h }
|
47
|
+
Nil() { self }
|
48
48
|
}
|
49
49
|
end
|
50
50
|
|
51
51
|
def tail
|
52
52
|
match {
|
53
|
-
Cons(_, t
|
54
|
-
Nil() {
|
53
|
+
Cons() { |_, t| t }
|
54
|
+
Nil() { raise EmptyListError }
|
55
55
|
}
|
56
56
|
end
|
57
57
|
|
58
58
|
def init
|
59
59
|
match {
|
60
|
-
Cons(
|
61
|
-
Cons(h, t
|
60
|
+
Cons(where { t.tail.null? } ) {|h, t| Cons.new(h, Nil.new) }
|
61
|
+
Cons() {|h, t| Cons.new(h, t.init) }
|
62
62
|
Nil() { raise EmptyListError }
|
63
63
|
}
|
64
64
|
end
|
65
65
|
|
66
66
|
def filter(&pred)
|
67
67
|
match {
|
68
|
-
Cons(
|
69
|
-
Cons(_, t
|
70
|
-
Nil() {
|
68
|
+
Cons(where { pred.(h) }) {|h, t| Cons.new(h, t.filter(&pred)) }
|
69
|
+
Cons() {|_, t| t.filter(&pred) }
|
70
|
+
Nil() { self }
|
71
71
|
}
|
72
72
|
end
|
73
73
|
|
@@ -76,21 +76,21 @@ Deterministic::impl(List) {
|
|
76
76
|
def find(&pred)
|
77
77
|
match {
|
78
78
|
Nil() { Deterministic::Option::None.new }
|
79
|
-
Cons(h, t
|
79
|
+
Cons() {|h, t| pred.(h) ? Deterministic::Option::Some.new(h) : t.find(&pred) }
|
80
80
|
}
|
81
81
|
end
|
82
82
|
|
83
83
|
def length
|
84
84
|
match {
|
85
|
-
Cons(h, t
|
85
|
+
Cons() {|h, t| 1 + t.length }
|
86
86
|
Nil() { 0 }
|
87
87
|
}
|
88
88
|
end
|
89
89
|
|
90
90
|
def map(&fn)
|
91
91
|
match {
|
92
|
-
Cons(h, t
|
93
|
-
Nil() {
|
92
|
+
Cons() {|h, t| Cons.new(fn.(h), t.map(&fn)) }
|
93
|
+
Nil() { self }
|
94
94
|
}
|
95
95
|
end
|
96
96
|
|
@@ -102,14 +102,14 @@ Deterministic::impl(List) {
|
|
102
102
|
match {
|
103
103
|
Nil() { start }
|
104
104
|
# foldl f z (x:xs) = foldl f (f z x) xs
|
105
|
-
Cons(h, t
|
105
|
+
Cons() {|h, t| t.foldl(fn.(start, h), &fn) }
|
106
106
|
}
|
107
107
|
end
|
108
108
|
|
109
109
|
def foldl1(&fn)
|
110
110
|
match {
|
111
111
|
Nil() { raise EmptyListError }
|
112
|
-
Cons(h, t
|
112
|
+
Cons() {|h, t| t.foldl(h, &fn)}
|
113
113
|
}
|
114
114
|
end
|
115
115
|
|
@@ -117,32 +117,32 @@ Deterministic::impl(List) {
|
|
117
117
|
match {
|
118
118
|
Nil() { start }
|
119
119
|
# foldr f z (x:xs) = f x (foldr f z xs)
|
120
|
-
Cons(h, t
|
120
|
+
Cons() {|h, t| fn.(h, t.foldr(start, &fn)) }
|
121
121
|
}
|
122
122
|
end
|
123
123
|
|
124
124
|
def foldr1(&fn)
|
125
125
|
match {
|
126
126
|
Nil() { raise EmptyListError }
|
127
|
-
Cons(
|
127
|
+
Cons(where { t.null? }) {|h, t| h }
|
128
128
|
# foldr1 f (x:xs) = f x (foldr1 f xs)
|
129
|
-
Cons(h, t
|
129
|
+
Cons() {|h, t| fn.(h, t.foldr1(&fn)) }
|
130
130
|
}
|
131
131
|
end
|
132
132
|
|
133
133
|
def take(n)
|
134
134
|
match {
|
135
|
-
Cons(
|
136
|
-
Cons(_, _
|
137
|
-
Nil() { raise EmptyListError}
|
135
|
+
Cons(where { n > 0 }) {|h, t| Cons.new(h, t.take(n - 1)) }
|
136
|
+
Cons() {|_, _| Nil.new }
|
137
|
+
Nil() { raise EmptyListError }
|
138
138
|
}
|
139
139
|
end
|
140
140
|
|
141
141
|
def drop(n)
|
142
142
|
match {
|
143
|
-
Cons(
|
144
|
-
Cons(_, _
|
145
|
-
Nil() { raise EmptyListError}
|
143
|
+
Cons(where { n > 0 }) {|h, t| t.drop(n - 1) }
|
144
|
+
Cons() {|_, _| self }
|
145
|
+
Nil() { raise EmptyListError }
|
146
146
|
}
|
147
147
|
end
|
148
148
|
|
@@ -153,31 +153,31 @@ Deterministic::impl(List) {
|
|
153
153
|
def any?(&pred)
|
154
154
|
match {
|
155
155
|
Nil() { false }
|
156
|
-
Cons(
|
157
|
-
Cons(h, t
|
156
|
+
Cons(where { t.null? }) {|h, t| pred.(h) }
|
157
|
+
Cons() {|h, t| pred.(h) || t.any?(&pred) }
|
158
158
|
}
|
159
159
|
end
|
160
160
|
|
161
161
|
def all?(&pred)
|
162
162
|
match {
|
163
163
|
Nil() { false }
|
164
|
-
Cons(
|
165
|
-
Cons(h, t
|
164
|
+
Cons(where { t.null? }) {|h, t| pred.(h) }
|
165
|
+
Cons() {|h, t| pred.(h) && t.all?(&pred) }
|
166
166
|
}
|
167
167
|
end
|
168
168
|
|
169
169
|
def reverse
|
170
170
|
match {
|
171
|
-
Nil() {
|
172
|
-
Cons(
|
173
|
-
Cons(h, t
|
171
|
+
Nil() { self }
|
172
|
+
Cons(where { t.null? }) {|_, t| self }
|
173
|
+
Cons() {|h, t| Cons.new(self.last.head, self.init.reverse) }
|
174
174
|
}
|
175
175
|
end
|
176
176
|
|
177
177
|
def to_s(joiner = ", ")
|
178
178
|
match {
|
179
179
|
Nil() { "Nil" }
|
180
|
-
Cons(head, tail
|
180
|
+
Cons() {|head, tail| head.to_s + joiner + tail.to_s }
|
181
181
|
}
|
182
182
|
end
|
183
183
|
}
|
data/spec/examples/list_spec.rb
CHANGED
@@ -13,9 +13,9 @@ describe List do
|
|
13
13
|
it "catches ignores guards with non-matching clauses" do
|
14
14
|
expect(
|
15
15
|
list.match {
|
16
|
-
Nil() {
|
17
|
-
Cons(
|
18
|
-
Cons(h, t
|
16
|
+
Nil() { list }
|
17
|
+
Cons(where { h == 0 }) {|h,t| h }
|
18
|
+
Cons() {|h, t| h }
|
19
19
|
}).to eq 1
|
20
20
|
end
|
21
21
|
|
@@ -23,15 +23,15 @@ describe List do
|
|
23
23
|
expect( # guard catched
|
24
24
|
list.match {
|
25
25
|
Nil() { raise "unreachable" }
|
26
|
-
Cons(
|
27
|
-
Cons(
|
26
|
+
Cons(where { h == 1 }) {|h,t| h + 1 }
|
27
|
+
Cons() {|h| h }
|
28
28
|
}).to eq 2
|
29
29
|
end
|
30
30
|
|
31
31
|
it "raises an error when no match was made" do
|
32
32
|
expect {
|
33
33
|
list.match {
|
34
|
-
Cons(
|
34
|
+
Cons(where { true == false }) {|_, _| 1 }
|
35
35
|
Nil(where { true == false }) { 0 }
|
36
36
|
}
|
37
37
|
}.to raise_error(Deterministic::Enum::MatchError)
|
@@ -40,7 +40,7 @@ describe List do
|
|
40
40
|
it "raises an error when the match is not exhaustive" do
|
41
41
|
expect {
|
42
42
|
list.match {
|
43
|
-
Cons(_, _
|
43
|
+
Cons() {|_, _| }
|
44
44
|
}
|
45
45
|
}.to raise_error(Deterministic::Enum::MatchError)
|
46
46
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
# TODO: dead code?
|
3
4
|
class Logger
|
4
5
|
alias :m :method
|
5
6
|
|
@@ -14,6 +15,7 @@ class Logger
|
|
14
15
|
private
|
15
16
|
attr_reader :repository
|
16
17
|
|
18
|
+
# TODO: this is never called; the matcher syntax is old
|
17
19
|
def validate(item)
|
18
20
|
return Failure(["Item cannot be empty"]) if item.blank?
|
19
21
|
return Failure(["Item must be a Hash"]) unless item.is_a?(Hash)
|
@@ -75,8 +75,8 @@ describe Deterministic::Option do
|
|
75
75
|
it "match" do
|
76
76
|
expect(
|
77
77
|
Some(0).match {
|
78
|
-
Some(
|
79
|
-
Some(
|
78
|
+
Some(where { s == 1 }) {|s| 99 }
|
79
|
+
Some(where { s == 0 }) {|s| s + 1 }
|
80
80
|
None() {}
|
81
81
|
}
|
82
82
|
).to eq 1
|
@@ -84,14 +84,14 @@ describe Deterministic::Option do
|
|
84
84
|
expect(
|
85
85
|
Some(1).match {
|
86
86
|
None() { 0 }
|
87
|
-
Some(
|
87
|
+
Some() {|s| 1 }
|
88
88
|
}
|
89
89
|
).to eq 1
|
90
90
|
|
91
91
|
expect(
|
92
92
|
Some(1).match {
|
93
93
|
None() { 0 }
|
94
|
-
Some(
|
94
|
+
Some(where { s.is_a? Fixnum }) {|s| 1 }
|
95
95
|
}
|
96
96
|
).to eq 1
|
97
97
|
|
data/spec/lib/enum_spec.rb
CHANGED
@@ -27,7 +27,7 @@ describe Deterministic::Enum do
|
|
27
27
|
expect(n).to be_a MyEnym
|
28
28
|
expect(n).to be_a MyEnym::Nullary
|
29
29
|
expect(n.name).to eq "Nullary"
|
30
|
-
expect { n.value }.to raise_error
|
30
|
+
expect { n.value }.to raise_error NoMethodError
|
31
31
|
expect(n.inspect).to eq "Nullary"
|
32
32
|
expect(n.to_s).to eq ""
|
33
33
|
expect(n.fmap { }).to eq n
|
@@ -77,8 +77,8 @@ describe Deterministic::Enum do
|
|
77
77
|
res =
|
78
78
|
MyEnym.match(b) {
|
79
79
|
Nullary() { 0 }
|
80
|
-
Unary(
|
81
|
-
Binary(x,
|
80
|
+
Unary() {|a| a }
|
81
|
+
Binary() {|x,y| [x, y] }
|
82
82
|
}
|
83
83
|
|
84
84
|
expect(res).to eq [1, 2]
|
@@ -86,8 +86,8 @@ describe Deterministic::Enum do
|
|
86
86
|
res =
|
87
87
|
b.match {
|
88
88
|
Nullary() { 0 }
|
89
|
-
Unary(
|
90
|
-
Binary(x,
|
89
|
+
Unary() {|a| a }
|
90
|
+
Binary() {|x,y| [x, y] }
|
91
91
|
}
|
92
92
|
|
93
93
|
expect(res).to eq [1, 2]
|
@@ -98,10 +98,10 @@ describe Deterministic::Enum do
|
|
98
98
|
}.to raise_error(NameError)
|
99
99
|
|
100
100
|
expect { b.match {
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
101
|
+
Nullary()
|
102
|
+
Unary()
|
103
|
+
Binary()
|
104
|
+
}
|
105
105
|
}.to raise_error ArgumentError, "No block given to `Nullary`"
|
106
106
|
end
|
107
107
|
end
|
data/spec/readme_spec.rb
CHANGED
@@ -20,19 +20,18 @@ Threenum = Deterministic::enum {
|
|
20
20
|
|
21
21
|
Deterministic::impl(Threenum) {
|
22
22
|
def sum
|
23
|
-
|
24
23
|
match {
|
25
|
-
Nullary()
|
26
|
-
Unary(
|
27
|
-
Binary(a, b
|
24
|
+
Nullary() { 0 }
|
25
|
+
Unary() { |u| u }
|
26
|
+
Binary() { |a, b| a + b }
|
28
27
|
}
|
29
28
|
end
|
30
29
|
|
31
30
|
def +(other)
|
32
31
|
match {
|
33
|
-
Nullary()
|
34
|
-
Unary(
|
35
|
-
Binary(a, b
|
32
|
+
Nullary() { other.sum }
|
33
|
+
Unary() { |a| self.sum + other.sum }
|
34
|
+
Binary() { |a, b| self.sum + other.sum }
|
36
35
|
}
|
37
36
|
end
|
38
37
|
}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: deterministic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Zolnierek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|