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