fear 0.11.0 → 1.0.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 +5 -5
- data/.gitignore +0 -1
- data/.rubocop.yml +18 -0
- data/.travis.yml +0 -3
- data/CHANGELOG.md +12 -1
- data/Gemfile +1 -0
- data/{gemfiles/dry_equalizer_0.2.1.gemfile.lock → Gemfile.lock} +21 -12
- data/README.md +594 -241
- data/Rakefile +166 -219
- data/benchmarks/README.md +1 -0
- data/benchmarks/dry_do_vs_fear_for.txt +11 -0
- data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +11 -0
- data/benchmarks/factorial.txt +16 -0
- data/benchmarks/fear_gaurd_and1_vs_new.txt +13 -0
- data/benchmarks/fear_gaurd_and2_vs_and.txt +13 -0
- data/benchmarks/fear_gaurd_and3_vs_and_and.txt +13 -0
- data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +11 -0
- data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +13 -0
- data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +14 -0
- data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +11 -0
- data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +11 -0
- data/examples/pattern_extracting.rb +15 -0
- data/examples/pattern_matching_binary_tree_set.rb +96 -0
- data/examples/pattern_matching_number_in_words.rb +54 -0
- data/fear.gemspec +4 -2
- data/lib/fear.rb +21 -4
- data/lib/fear/either.rb +77 -59
- data/lib/fear/either_api.rb +21 -0
- data/lib/fear/empty_partial_function.rb +1 -1
- data/lib/fear/extractor.rb +108 -0
- data/lib/fear/extractor/anonymous_array_splat_matcher.rb +8 -0
- data/lib/fear/extractor/any_matcher.rb +15 -0
- data/lib/fear/extractor/array_head_matcher.rb +34 -0
- data/lib/fear/extractor/array_matcher.rb +38 -0
- data/lib/fear/extractor/array_splat_matcher.rb +14 -0
- data/lib/fear/extractor/empty_list_matcher.rb +18 -0
- data/lib/fear/extractor/extractor_matcher.rb +42 -0
- data/lib/fear/extractor/grammar.rb +201 -0
- data/lib/fear/extractor/grammar.treetop +129 -0
- data/lib/fear/extractor/identifier_matcher.rb +16 -0
- data/lib/fear/extractor/matcher.rb +54 -0
- data/lib/fear/extractor/matcher/and.rb +36 -0
- data/lib/fear/extractor/named_array_splat_matcher.rb +15 -0
- data/lib/fear/extractor/pattern.rb +55 -0
- data/lib/fear/extractor/typed_identifier_matcher.rb +24 -0
- data/lib/fear/extractor/value_matcher.rb +17 -0
- data/lib/fear/extractor_api.rb +33 -0
- data/lib/fear/failure.rb +32 -10
- data/lib/fear/for.rb +14 -69
- data/lib/fear/for_api.rb +66 -0
- data/lib/fear/future.rb +414 -0
- data/lib/fear/future_api.rb +19 -0
- data/lib/fear/left.rb +8 -0
- data/lib/fear/none.rb +17 -8
- data/lib/fear/option.rb +55 -49
- data/lib/fear/option_api.rb +38 -0
- data/lib/fear/partial_function.rb +9 -12
- data/lib/fear/partial_function/empty.rb +1 -1
- data/lib/fear/partial_function/guard.rb +8 -20
- data/lib/fear/partial_function/lifted.rb +1 -0
- data/lib/fear/partial_function_class.rb +10 -0
- data/lib/fear/pattern_match.rb +10 -0
- data/lib/fear/pattern_matching_api.rb +35 -11
- data/lib/fear/promise.rb +87 -0
- data/lib/fear/right.rb +8 -0
- data/lib/fear/some.rb +22 -3
- data/lib/fear/success.rb +22 -1
- data/lib/fear/try.rb +82 -67
- data/lib/fear/try_api.rb +31 -0
- data/lib/fear/unit.rb +28 -0
- data/lib/fear/version.rb +1 -1
- data/spec/fear/done_spec.rb +3 -3
- data/spec/fear/either/mixin_spec.rb +15 -0
- data/spec/fear/either_pattern_match_spec.rb +10 -12
- data/spec/fear/extractor/array_matcher_spec.rb +228 -0
- data/spec/fear/extractor/extractor_matcher_spec.rb +151 -0
- data/spec/fear/extractor/grammar_array_spec.rb +23 -0
- data/spec/fear/extractor/identified_matcher_spec.rb +47 -0
- data/spec/fear/extractor/identifier_matcher_spec.rb +66 -0
- data/spec/fear/extractor/pattern_spec.rb +32 -0
- data/spec/fear/extractor/typed_identifier_matcher_spec.rb +62 -0
- data/spec/fear/extractor/value_matcher_number_spec.rb +77 -0
- data/spec/fear/extractor/value_matcher_string_spec.rb +86 -0
- data/spec/fear/extractor/value_matcher_symbol_spec.rb +69 -0
- data/spec/fear/extractor_api_spec.rb +113 -0
- data/spec/fear/extractor_spec.rb +59 -0
- data/spec/fear/failure_spec.rb +73 -13
- data/spec/fear/for_spec.rb +35 -35
- data/spec/fear/future_spec.rb +466 -0
- data/spec/fear/guard_spec.rb +4 -4
- data/spec/fear/left_spec.rb +40 -14
- data/spec/fear/none_spec.rb +28 -12
- data/spec/fear/option/mixin_spec.rb +37 -0
- data/spec/fear/option_pattern_match_spec.rb +7 -9
- data/spec/fear/partial_function_spec.rb +25 -3
- data/spec/fear/pattern_match_spec.rb +33 -1
- data/spec/fear/promise_spec.rb +94 -0
- data/spec/fear/right_spec.rb +37 -9
- data/spec/fear/some_spec.rb +32 -6
- data/spec/fear/success_spec.rb +32 -4
- data/spec/fear/try/mixin_spec.rb +17 -0
- data/spec/fear/try_pattern_match_spec.rb +8 -10
- data/spec/spec_helper.rb +1 -1
- metadata +115 -20
- data/Appraisals +0 -32
- data/gemfiles/dry_equalizer_0.1.0.gemfile +0 -8
- data/gemfiles/dry_equalizer_0.1.0.gemfile.lock +0 -82
- data/gemfiles/dry_equalizer_0.2.1.gemfile +0 -8
- data/lib/fear/done.rb +0 -22
- data/spec/fear/option_spec.rb +0 -15
@@ -0,0 +1 @@
|
|
1
|
+
See Rakefile
|
@@ -0,0 +1,11 @@
|
|
1
|
+
> bundle exec rake perf:dry:do_vs_fear_for
|
2
|
+
Warming up --------------------------------------
|
3
|
+
Dry 31.375k i/100ms
|
4
|
+
Fear 24.131k i/100ms
|
5
|
+
Calculating -------------------------------------
|
6
|
+
Dry 360.990k (± 2.7%) i/s - 1.820M in 5.045364s
|
7
|
+
Fear 7.212B (±27.2%) i/s - 27.210B in 4.656760s
|
8
|
+
|
9
|
+
Comparison:
|
10
|
+
Fear: 7212177713.5 i/s
|
11
|
+
Dry: 360989.8 i/s - 19978.90x slower
|
@@ -0,0 +1,11 @@
|
|
1
|
+
> bundle exec rake perf:dry:some_fmap_vs_fear_some_map
|
2
|
+
Warming up --------------------------------------
|
3
|
+
Dry 68.154k i/100ms
|
4
|
+
Fear 151.093k i/100ms
|
5
|
+
Calculating -------------------------------------
|
6
|
+
Dry 884.315k (± 4.1%) i/s - 4.430M in 5.018931s
|
7
|
+
Fear 2.481M (± 4.1%) i/s - 12.390M in 5.003728s
|
8
|
+
|
9
|
+
Comparison:
|
10
|
+
Fear: 2480589.0 i/s
|
11
|
+
Dry: 884315.1 i/s - 2.81x slower
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# Depends on `n`, for 100!:
|
2
|
+
|
3
|
+
> bundle exec rake perf:pattern_matching:factorial
|
4
|
+
Warming up --------------------------------------
|
5
|
+
Proc 2.395k i/100ms
|
6
|
+
Fear 312.000 i/100ms
|
7
|
+
Qo 122.000 i/100ms
|
8
|
+
Calculating -------------------------------------
|
9
|
+
Proc 26.620k (± 2.9%) i/s - 134.120k in 5.042738s
|
10
|
+
Fear 3.219k (± 4.1%) i/s - 16.224k in 5.049215s
|
11
|
+
Qo 1.250k (± 4.8%) i/s - 6.344k in 5.090745s
|
12
|
+
|
13
|
+
Comparison:
|
14
|
+
Proc: 26620.3 i/s
|
15
|
+
Fear: 3219.4 i/s - 8.27x slower
|
16
|
+
Qo: 1249.6 i/s - 21.30x slower
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# It verifies that optimization for Partial Functions with only one guard actually works.
|
2
|
+
|
3
|
+
> bundle exec rake perf:fear:guard:and1_vs_new
|
4
|
+
Warming up --------------------------------------
|
5
|
+
Guard.new 173.019k i/100ms
|
6
|
+
Guard.and1 268.379k i/100ms
|
7
|
+
Calculating -------------------------------------
|
8
|
+
Guard.new 171.291B (± 8.4%) i/s - 561.437B
|
9
|
+
Guard.and1 266.882B (± 5.9%) i/s - 751.316B
|
10
|
+
|
11
|
+
Comparison:
|
12
|
+
Guard.and1: 266881817299.6 i/s
|
13
|
+
Guard.new: 171291386467.1 i/s - 1.56x slower
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# It verifies that optimization for Partial Functions with two guards actually works.
|
2
|
+
|
3
|
+
> bundle exec rake perf:fear:guard:and2_vs_guard_and_guard
|
4
|
+
Warming up --------------------------------------
|
5
|
+
and2 224.836k i/100ms
|
6
|
+
Guard#and 211.833k i/100ms
|
7
|
+
Calculating -------------------------------------
|
8
|
+
and2 224.457B (± 3.8%) i/s - 651.564B
|
9
|
+
Guard#and 211.486B (± 3.7%) i/s - 667.936B
|
10
|
+
|
11
|
+
Comparison:
|
12
|
+
and2: 224457051906.8 i/s
|
13
|
+
Guard#and: 211485786834.4 i/s - same-ish: difference falls within error
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# It verifies that optimization for Partial Functions with three guards actually works.
|
2
|
+
|
3
|
+
> bundle exec rake perf:fear:guard:and3_vs_and_and
|
4
|
+
Warming up --------------------------------------
|
5
|
+
Guard.and3 236.318k i/100ms
|
6
|
+
Guard#and 170.369k i/100ms
|
7
|
+
Calculating -------------------------------------
|
8
|
+
Guard.and3 235.992B (± 3.4%) i/s - 791.166B
|
9
|
+
Guard#and 169.998B (± 4.0%) i/s - 640.637B
|
10
|
+
|
11
|
+
Comparison:
|
12
|
+
Guard.and3: 235992292688.6 i/s
|
13
|
+
Guard#and: 169997755111.1 i/s - 1.39x slower
|
@@ -0,0 +1,11 @@
|
|
1
|
+
> bundle exec rake perf:fear:pattern_extracting_with_vs_without_cache
|
2
|
+
Warming up --------------------------------------
|
3
|
+
With cache 1.555k i/100ms
|
4
|
+
Without cache 164.000 i/100ms
|
5
|
+
Calculating -------------------------------------
|
6
|
+
With cache 26.159M (±14.7%) i/s - 124.607M in 4.958175s
|
7
|
+
Without cache 278.285k (±10.5%) i/s - 1.368M in 4.993567s
|
8
|
+
|
9
|
+
Comparison:
|
10
|
+
With cache: 26159162.8 i/s
|
11
|
+
Without cache: 278285.4 i/s - 94.00x slower
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# So, it's better to avoid building pattern match in runtime
|
2
|
+
|
3
|
+
> bundle exec rake perf:fear:pattern_matching_construction_vs_execution
|
4
|
+
Warming up --------------------------------------
|
5
|
+
construction 24.425k i/100ms
|
6
|
+
execution 80.516k i/100ms
|
7
|
+
Calculating -------------------------------------
|
8
|
+
construction 267.887k (± 4.9%) i/s - 1.343M in 5.029005s
|
9
|
+
execution 1.085M (± 2.6%) i/s - 5.475M in 5.049775s
|
10
|
+
|
11
|
+
Comparison:
|
12
|
+
execution: 1084968.2 i/s
|
13
|
+
construction: 267886.6 i/s - 4.05x slower
|
@@ -0,0 +1,14 @@
|
|
1
|
+
> bundle exec rake perf:pattern_matching:dry_vs_qo_vs_fear_try
|
2
|
+
Warming up --------------------------------------
|
3
|
+
Qo 2.958k i/100ms
|
4
|
+
Fear 7.127k i/100ms
|
5
|
+
Dr::Matcher 13.079k i/100ms
|
6
|
+
Calculating -------------------------------------
|
7
|
+
Qo 38.872k (± 3.1%) i/s - 195.228k in 5.027249s
|
8
|
+
Fear 88.756k (± 3.7%) i/s - 449.001k in 5.066471s
|
9
|
+
Dr::Matcher 166.700k (± 3.0%) i/s - 837.056k in 5.026408s
|
10
|
+
|
11
|
+
Comparison:
|
12
|
+
Dr::Matcher: 166699.7 i/s
|
13
|
+
Fear: 88755.7 i/s - 1.88x slower
|
14
|
+
Qo: 38871.9 i/s - 4.29x slower
|
@@ -0,0 +1,11 @@
|
|
1
|
+
> bundle exec rake perf:pattern_matching:qo_vs_fear_pattern_extraction
|
2
|
+
Warming up --------------------------------------
|
3
|
+
Qo 12.352k i/100ms
|
4
|
+
Fear 6.841k i/100ms
|
5
|
+
Calculating -------------------------------------
|
6
|
+
Qo 142.416k (± 4.3%) i/s - 716.416k in 5.040430s
|
7
|
+
Fear 88.179k (± 4.8%) i/s - 444.665k in 5.055908s
|
8
|
+
|
9
|
+
Comparison:
|
10
|
+
Qo: 142415.6 i/s
|
11
|
+
Fear: 88179.5 i/s - 1.62x slower
|
@@ -0,0 +1,11 @@
|
|
1
|
+
> bundle exec rake perf:pattern_matching:qo_vs_fear_try_execution
|
2
|
+
Warming up --------------------------------------
|
3
|
+
Qo 10.079k i/100ms
|
4
|
+
Fear 25.868k i/100ms
|
5
|
+
Calculating -------------------------------------
|
6
|
+
Qo 142.209k (± 3.8%) i/s - 715.609k in 5.039975s
|
7
|
+
Fear 395.999k (± 2.5%) i/s - 1.992M in 5.033046s
|
8
|
+
|
9
|
+
Comparison:
|
10
|
+
Fear: 395999.2 i/s
|
11
|
+
Qo: 142209.4 i/s - 2.78x slower
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'fear'
|
2
|
+
|
3
|
+
User = Struct.new(:id, :name, :admin)
|
4
|
+
|
5
|
+
matcher = Fear.matcher do |m|
|
6
|
+
m.xcase('User(_, name, true)') do |name:|
|
7
|
+
puts "Hi #{name}, you are welcome"
|
8
|
+
end
|
9
|
+
m.xcase('User(_, _, false)') do
|
10
|
+
puts 'Only admins allowed here'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
matcher.call User.new(1, 'Jane', true)
|
15
|
+
matcher.call User.new(1, 'John', false)
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'fear'
|
2
|
+
|
3
|
+
# @example Usage
|
4
|
+
# set = BinaryTreeSet.new
|
5
|
+
# set.add(4)
|
6
|
+
# set.includes?(4) #=> true
|
7
|
+
# set.includes?(5) #=> false
|
8
|
+
# set.delete(4)
|
9
|
+
# set.includes?(4) #=> false
|
10
|
+
#
|
11
|
+
class BinaryTreeSet
|
12
|
+
Position = Module.new
|
13
|
+
Right = Module.new.include(Position)
|
14
|
+
Left = Module.new.include(Position)
|
15
|
+
|
16
|
+
def initialize(elem = 0, removed: true)
|
17
|
+
@elem = elem
|
18
|
+
@removed = removed
|
19
|
+
@subtrees = {}
|
20
|
+
end
|
21
|
+
attr_reader :elem, :subtrees
|
22
|
+
attr_accessor :removed
|
23
|
+
private :elem
|
24
|
+
private :removed
|
25
|
+
private :subtrees
|
26
|
+
|
27
|
+
# @param value [Integer]
|
28
|
+
# @return [Boolean]
|
29
|
+
def includes?(value)
|
30
|
+
Fear.match(value) do |m|
|
31
|
+
m.case(elem) { !removed }
|
32
|
+
m.case(->(x) { x > elem }) { |v| includes_in_leaf?(Right, v) }
|
33
|
+
m.case(->(x) { x < elem }) { |v| includes_in_leaf?(Left, v) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @param position [Position]
|
38
|
+
# @param value [Integer]
|
39
|
+
# @return [Boolean]
|
40
|
+
private def includes_in_leaf?(position, value)
|
41
|
+
leaf(position).match do |m|
|
42
|
+
m.some { |leaf| leaf.includes?(value) }
|
43
|
+
m.none { false }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# @param value [Integer]
|
48
|
+
# @return [void]
|
49
|
+
def add(value)
|
50
|
+
Fear.match(value) do |m|
|
51
|
+
m.case(elem) { self.removed = false }
|
52
|
+
m.case(->(x) { x > elem }) { |v| add_to_leaf(Right, v) }
|
53
|
+
m.case(->(x) { x < elem }) { |v| add_to_leaf(Left, v) }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param position [Position]
|
58
|
+
# @param value [Integer]
|
59
|
+
# @return [void]
|
60
|
+
private def add_to_leaf(position, value)
|
61
|
+
leaf(position).match do |m|
|
62
|
+
m.some { |leaf| leaf.add(value) }
|
63
|
+
m.none { subtrees[position] = BinaryTreeSet.new(value, removed: false) }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @param value [Integer]
|
68
|
+
# @return [void]
|
69
|
+
def delete(value)
|
70
|
+
Fear.match(value) do |m|
|
71
|
+
m.case(elem) { self.removed = true }
|
72
|
+
m.case(->(x) { x > elem }) { |v| delete_from_leaf(Right, v) }
|
73
|
+
m.case(->(x) { x < elem }) { |v| delete_from_leaf(Left, v) }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @param position [Position]
|
78
|
+
# @param value [Integer]
|
79
|
+
# @return [void]
|
80
|
+
private def delete_from_leaf(position, value)
|
81
|
+
leaf(position).match do |m|
|
82
|
+
m.some { |leaf| leaf.delete(value) }
|
83
|
+
m.none { subtrees[position] = BinaryTreeSet.new(value, removed: true) }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# @param position [Position]
|
88
|
+
# @return [Fear::Option<BinaryTreeSet>]
|
89
|
+
private def leaf(position)
|
90
|
+
if subtrees.key?(position)
|
91
|
+
Fear.some(subtrees[position])
|
92
|
+
else
|
93
|
+
Fear.none
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'fear'
|
2
|
+
|
3
|
+
class ToWords
|
4
|
+
CONVERTER = Fear.matcher do |m|
|
5
|
+
{
|
6
|
+
0 => 'zero',
|
7
|
+
1 => 'one',
|
8
|
+
2 => 'two',
|
9
|
+
3 => 'three',
|
10
|
+
4 => 'four',
|
11
|
+
5 => 'five',
|
12
|
+
6 => 'six',
|
13
|
+
7 => 'seven',
|
14
|
+
8 => 'eight',
|
15
|
+
9 => 'nine',
|
16
|
+
10 => 'ten',
|
17
|
+
11 => 'eleven',
|
18
|
+
12 => 'twelve',
|
19
|
+
13 => 'thirteen',
|
20
|
+
14 => 'fourteen',
|
21
|
+
15 => 'fifteen',
|
22
|
+
16 => 'sixteen',
|
23
|
+
17 => 'seventeen',
|
24
|
+
18 => 'eighteen',
|
25
|
+
19 => 'nineteen',
|
26
|
+
20 => 'twenty',
|
27
|
+
30 => 'thirty',
|
28
|
+
40 => 'forty',
|
29
|
+
50 => 'fifty',
|
30
|
+
60 => 'sixty',
|
31
|
+
70 => 'seventy',
|
32
|
+
80 => 'eighty',
|
33
|
+
90 => 'ninety',
|
34
|
+
}.each_pair do |number, in_words|
|
35
|
+
m.case(number) { in_words }
|
36
|
+
end
|
37
|
+
m.case(->(n) { n < 0 }) { |n| "minus #{CONVERTER.call(-n)}" }
|
38
|
+
m.case(->(n) { n < 100 }) { |n| "#{CONVERTER.call((n / 10) * 10)}-#{CONVERTER.call(n % 10)}" }
|
39
|
+
m.case(->(n) { n < 200 }) { |n| "one hundred #{CONVERTER.call(n % 100)}" }
|
40
|
+
m.case(->(n) { n < 1_000 }) { |n| "#{CONVERTER.call(n / 100)} hundreds #{CONVERTER.call(n % 100)}" }
|
41
|
+
m.case(->(n) { n < 2_000 }) { |n| "one thousand #{CONVERTER.call(n % 1000)}" }
|
42
|
+
m.case(->(n) { n < 1_000_000 }) { |n| "#{CONVERTER.call(n / 1_000)} thousands #{CONVERTER.call(n % 1_000)}" }
|
43
|
+
m.else { |n| raise "#{n} too big " }
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.call(number)
|
47
|
+
Fear.case(Integer, &:itself).and_then(CONVERTER).call(number)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
ToWords.call(99) #=> 'ninety-nine'
|
52
|
+
ToWords.call(133) #=> 'one hundred thirty-three
|
53
|
+
ToWords.call(777) #=> 'seven hundreds seventy-seven'
|
54
|
+
ToWords.call(254_555) #=> 'two hundreds fifty-four thousands five hundreds fifty-five'
|
data/fear.gemspec
CHANGED
@@ -24,12 +24,14 @@ Gem::Specification.new do |spec|
|
|
24
24
|
Successfully installed fear-#{Fear::VERSION}
|
25
25
|
MSG
|
26
26
|
|
27
|
-
spec.add_runtime_dependency '
|
27
|
+
spec.add_runtime_dependency 'lru_redux'
|
28
|
+
spec.add_runtime_dependency 'treetop'
|
28
29
|
|
29
|
-
spec.add_development_dependency 'appraisal'
|
30
30
|
spec.add_development_dependency 'benchmark-ips'
|
31
31
|
spec.add_development_dependency 'bundler'
|
32
|
+
spec.add_development_dependency 'concurrent-ruby'
|
32
33
|
spec.add_development_dependency 'dry-matcher'
|
34
|
+
spec.add_development_dependency 'dry-monads'
|
33
35
|
spec.add_development_dependency 'qo'
|
34
36
|
spec.add_development_dependency 'rake', '~> 10.0'
|
35
37
|
spec.add_development_dependency 'rspec', '~> 3.1'
|
data/lib/fear.rb
CHANGED
@@ -1,19 +1,34 @@
|
|
1
|
-
require '
|
2
|
-
require 'fear/
|
1
|
+
require 'fear/either_api'
|
2
|
+
require 'fear/extractor_api'
|
3
|
+
require 'fear/for_api'
|
4
|
+
require 'fear/future_api'
|
5
|
+
require 'fear/option_api'
|
3
6
|
require 'fear/pattern_matching_api'
|
7
|
+
require 'fear/try_api'
|
8
|
+
require 'fear/version'
|
4
9
|
|
5
10
|
module Fear
|
6
11
|
Error = Class.new(StandardError)
|
7
|
-
|
12
|
+
IllegalStateException = Class.new(Error)
|
8
13
|
MatchError = Class.new(Error)
|
14
|
+
NoSuchElementError = Class.new(Error)
|
15
|
+
PatternSyntaxError = Class.new(Error)
|
16
|
+
|
17
|
+
extend EitherApi
|
18
|
+
extend ExtractorApi
|
19
|
+
extend ForApi
|
20
|
+
extend FutureApi
|
21
|
+
extend OptionApi
|
9
22
|
extend PatternMatchingApi
|
23
|
+
extend TryApi
|
10
24
|
|
11
25
|
autoload :EmptyPartialFunction, 'fear/empty_partial_function'
|
12
26
|
autoload :PartialFunction, 'fear/partial_function'
|
13
27
|
autoload :PartialFunctionClass, 'fear/partial_function_class'
|
14
28
|
autoload :PatternMatch, 'fear/pattern_match'
|
29
|
+
autoload :Extractor, 'fear/extractor'
|
15
30
|
|
16
|
-
autoload :
|
31
|
+
autoload :Unit, 'fear/unit'
|
17
32
|
autoload :For, 'fear/for'
|
18
33
|
autoload :RightBiased, 'fear/right_biased'
|
19
34
|
autoload :Utils, 'fear/utils'
|
@@ -40,6 +55,8 @@ module Fear
|
|
40
55
|
autoload :Right, 'fear/right'
|
41
56
|
autoload :RightPatternMatch, 'fear/right_pattern_match'
|
42
57
|
|
58
|
+
autoload :Future, 'fear/future'
|
59
|
+
|
43
60
|
module Mixin
|
44
61
|
include Either::Mixin
|
45
62
|
include For::Mixin
|
data/lib/fear/either.rb
CHANGED
@@ -14,9 +14,9 @@ module Fear
|
|
14
14
|
# @example
|
15
15
|
# in = Readline.readline('Type Either a string or an Int: ', true)
|
16
16
|
# result = begin
|
17
|
-
#
|
17
|
+
# Fear.right(Integer(in))
|
18
18
|
# rescue ArgumentError
|
19
|
-
#
|
19
|
+
# Fear.left(in)
|
20
20
|
# end
|
21
21
|
#
|
22
22
|
# result.match do |m|
|
@@ -40,21 +40,21 @@ module Fear
|
|
40
40
|
# @yieldreturn [any]
|
41
41
|
# @return [any]
|
42
42
|
# @example
|
43
|
-
#
|
44
|
-
#
|
43
|
+
# Fear.right(42).get_or_else { 24/2 } #=> 42
|
44
|
+
# Fear.left('undefined').get_or_else { 24/2 } #=> 12
|
45
45
|
# @overload get_or_else(default)
|
46
46
|
# @return [any]
|
47
47
|
# @example
|
48
|
-
#
|
49
|
-
#
|
48
|
+
# Fear.right(42).get_or_else(12) #=> 42
|
49
|
+
# Fear.left('undefined').get_or_else(12) #=> 12
|
50
50
|
#
|
51
51
|
# @!method or_else(&alternative)
|
52
52
|
# Returns this +Right+ or the given alternative if this is a +Left+.
|
53
53
|
# @return [Either]
|
54
54
|
# @example
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
55
|
+
# Fear.right(42).or_else { Fear.right(21) } #=> Fear.right(42)
|
56
|
+
# Fear.left('unknown').or_else { Fear.right(21) } #=> Fear.right(21)
|
57
|
+
# Fear.left('unknown').or_else { Fear.left('empty') } #=> Fear.left('empty')
|
58
58
|
#
|
59
59
|
# @!method include?(other_value)
|
60
60
|
# Returns +true+ if +Right+ has an element that is equal
|
@@ -62,9 +62,9 @@ module Fear
|
|
62
62
|
# @param [any]
|
63
63
|
# @return [Boolean]
|
64
64
|
# @example
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
65
|
+
# Fear.right(17).include?(17) #=> true
|
66
|
+
# Fear.right(17).include?(7) #=> false
|
67
|
+
# Fear.left('undefined').include?(17) #=> false
|
68
68
|
#
|
69
69
|
# @!method each(&block)
|
70
70
|
# Performs the given block if this is a +Right+.
|
@@ -72,11 +72,11 @@ module Fear
|
|
72
72
|
# @yieldreturn [void]
|
73
73
|
# @return [Option] itself
|
74
74
|
# @example
|
75
|
-
#
|
75
|
+
# Fear.right(17).each do |value|
|
76
76
|
# puts value
|
77
77
|
# end #=> prints 17
|
78
78
|
#
|
79
|
-
#
|
79
|
+
# Fear.left('undefined').each do |value|
|
80
80
|
# puts value
|
81
81
|
# end #=> does nothing
|
82
82
|
#
|
@@ -86,8 +86,8 @@ module Fear
|
|
86
86
|
# @yieldparam [any] value
|
87
87
|
# @yieldreturn [any]
|
88
88
|
# @example
|
89
|
-
#
|
90
|
-
#
|
89
|
+
# Fear.right(42).map { |v| v/2 } #=> Fear.right(21)
|
90
|
+
# Fear.left('undefined').map { |v| v/2 } #=> Fear.left('undefined')
|
91
91
|
#
|
92
92
|
# @!method flat_map(&block)
|
93
93
|
# Returns the given block applied to the value from this +Right+
|
@@ -96,16 +96,16 @@ module Fear
|
|
96
96
|
# @yieldreturn [Option]
|
97
97
|
# @return [Option]
|
98
98
|
# @example
|
99
|
-
#
|
100
|
-
#
|
99
|
+
# Fear.right(42).flat_map { |v| Fear.right(v/2) } #=> Fear.right(21)
|
100
|
+
# Fear.left('undefined').flat_map { |v| Fear.right(v/2) } #=> Fear.left('undefined')
|
101
101
|
#
|
102
102
|
# @!method to_option
|
103
103
|
# Returns an +Some+ containing the +Right+ value or a +None+ if
|
104
104
|
# this is a +Left+.
|
105
105
|
# @return [Option]
|
106
106
|
# @example
|
107
|
-
#
|
108
|
-
#
|
107
|
+
# Fear.right(42).to_option #=> Fear.some(21)
|
108
|
+
# Fear.left('undefined').to_option #=> Fear.none()
|
109
109
|
#
|
110
110
|
# @!method any?(&predicate)
|
111
111
|
# Returns +false+ if +Left+ or returns the result of the
|
@@ -114,9 +114,9 @@ module Fear
|
|
114
114
|
# @yieldreturn [Boolean]
|
115
115
|
# @return [Boolean]
|
116
116
|
# @example
|
117
|
-
#
|
118
|
-
#
|
119
|
-
#
|
117
|
+
# Fear.right(12).any?( |v| v > 10) #=> true
|
118
|
+
# Fear.right(7).any?( |v| v > 10) #=> false
|
119
|
+
# Fear.left('undefined').any?( |v| v > 10) #=> false
|
120
120
|
#
|
121
121
|
# -----
|
122
122
|
#
|
@@ -125,16 +125,16 @@ module Fear
|
|
125
125
|
# @note this method is also aliased as +#success?+
|
126
126
|
# @return [Boolean]
|
127
127
|
# @example
|
128
|
-
#
|
129
|
-
#
|
128
|
+
# Fear.right(42).right? #=> true
|
129
|
+
# Fear.left('err').right? #=> false
|
130
130
|
#
|
131
131
|
# @!method left?
|
132
132
|
# Returns +true+ if this is a +Left+, +false+ otherwise.
|
133
133
|
# @note this method is also aliased as +#failure?+
|
134
134
|
# @return [Boolean]
|
135
135
|
# @example
|
136
|
-
#
|
137
|
-
#
|
136
|
+
# Fear.right(42).left? #=> false
|
137
|
+
# Fear.left('err').left? #=> true
|
138
138
|
#
|
139
139
|
# @!method select_or_else(default, &predicate)
|
140
140
|
# Returns +Left+ of the default if the given predicate
|
@@ -144,10 +144,10 @@ module Fear
|
|
144
144
|
# @yieldreturn [Boolean]
|
145
145
|
# @return [Either]
|
146
146
|
# @example
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
147
|
+
# Fear.right(12).select_or_else(-1, &:even?) #=> Fear.right(12)
|
148
|
+
# Fear.right(7).select_or_else(-1, &:even?) #=> Fear.left(-1)
|
149
|
+
# Fear.left(12).select_or_else(-1, &:even?) #=> Fear.left(12)
|
150
|
+
# Fear.left(12).select_or_else(-> { -1 }, &:even?) #=> Fear.left(12)
|
151
151
|
#
|
152
152
|
# @!method select(&predicate)
|
153
153
|
# Returns +Left+ of value if the given predicate
|
@@ -156,10 +156,10 @@ module Fear
|
|
156
156
|
# @yieldreturn [Boolean]
|
157
157
|
# @return [Either]
|
158
158
|
# @example
|
159
|
-
#
|
160
|
-
#
|
161
|
-
#
|
162
|
-
#
|
159
|
+
# Fear.right(12).select(&:even?) #=> Fear.right(12)
|
160
|
+
# Fear.right(7).select(&:even?) #=> Fear.left(7)
|
161
|
+
# Fear.left(12).select(&:even?) #=> Fear.left(12)
|
162
|
+
# Fear.left(7).select(&:even?) #=> Fear.left(7)
|
163
163
|
#
|
164
164
|
# @!method reject(&predicate)
|
165
165
|
# Returns +Left+ of value if the given predicate holds for the
|
@@ -168,17 +168,17 @@ module Fear
|
|
168
168
|
# @yieldreturn [Boolean]
|
169
169
|
# @return [Either]
|
170
170
|
# @example
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
174
|
-
#
|
171
|
+
# Fear.right(12).reject(&:even?) #=> Fear.left(12)
|
172
|
+
# Fear.right(7).reject(&:even?) #=> Fear.right(7)
|
173
|
+
# Fear.left(12).reject(&:even?) #=> Fear.left(12)
|
174
|
+
# Fear.left(7).reject(&:even?) #=> Fear.left(7)
|
175
175
|
#
|
176
176
|
# @!method swap
|
177
177
|
# If this is a +Left+, then return the left value in +Right+ or vice versa.
|
178
178
|
# @return [Either]
|
179
179
|
# @example
|
180
|
-
#
|
181
|
-
#
|
180
|
+
# Fear.left('left').swap #=> Fear.right('left')
|
181
|
+
# Fear.right('right').swap #=> Fear.left('left')
|
182
182
|
#
|
183
183
|
# @!method reduce(reduce_left, reduce_right)
|
184
184
|
# Applies +reduce_left+ if this is a +Left+ or +reduce_right+ if
|
@@ -204,10 +204,10 @@ module Fear
|
|
204
204
|
# @return [Either]
|
205
205
|
# @raise [TypeError] if it does not contain +Either+.
|
206
206
|
# @example
|
207
|
-
#
|
208
|
-
#
|
209
|
-
#
|
210
|
-
#
|
207
|
+
# Fear.right(Fear.right(12)).join_right #=> Fear.right(12)
|
208
|
+
# Fear.right(Fear.left("flower")).join_right #=> Fear.left("flower")
|
209
|
+
# Fear.left("flower").join_right #=> Fear.left("flower")
|
210
|
+
# Fear.left(Fear.right("flower")).join_right #=> Fear.left(Fear.right("flower"))
|
211
211
|
#
|
212
212
|
# @!method join_right
|
213
213
|
# Joins an +Either+ through +Left+. This method requires
|
@@ -217,16 +217,16 @@ module Fear
|
|
217
217
|
# @return [Either]
|
218
218
|
# @raise [TypeError] if it does not contain +Either+.
|
219
219
|
# @example
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
220
|
+
# Fear.left(Fear.right("flower")).join_left #=> Fear.right("flower")
|
221
|
+
# Fear.left(Fear.left(12)).join_left #=> Fear.left(12)
|
222
|
+
# Fear.right("daisy").join_left #=> Fear.right("daisy")
|
223
|
+
# Fear.right(Fear.left("daisy")).join_left #=> Fear.right(Fear.left("daisy"))
|
224
224
|
#
|
225
225
|
# @!method match(&matcher)
|
226
226
|
# Pattern match against this +Either+
|
227
227
|
# @yield matcher [Fear::EitherPatternMatch]
|
228
228
|
# @example
|
229
|
-
#
|
229
|
+
# either.match do |m|
|
230
230
|
# m.right(Integer) do |x|
|
231
231
|
# x * 2
|
232
232
|
# end
|
@@ -242,8 +242,6 @@ module Fear
|
|
242
242
|
# @see https://github.com/scala/scala/blob/2.12.x/src/library/scala/util/Either.scala
|
243
243
|
#
|
244
244
|
module Either
|
245
|
-
include Dry::Equalizer(:value)
|
246
|
-
|
247
245
|
# @private
|
248
246
|
def left_class
|
249
247
|
Left
|
@@ -261,6 +259,20 @@ module Fear
|
|
261
259
|
attr_reader :value
|
262
260
|
protected :value
|
263
261
|
|
262
|
+
# @param other [Any]
|
263
|
+
# @return [Boolean]
|
264
|
+
def ==(other)
|
265
|
+
other.is_a?(self.class) && value == other.value
|
266
|
+
end
|
267
|
+
|
268
|
+
# @return [String]
|
269
|
+
def inspect
|
270
|
+
"#<#{self.class} value=#{value.inspect}>"
|
271
|
+
end
|
272
|
+
|
273
|
+
# @return [String]
|
274
|
+
alias to_s inspect
|
275
|
+
|
264
276
|
class << self
|
265
277
|
# Build pattern matcher to be used later, despite off
|
266
278
|
# +Either#match+ method, id doesn't apply matcher immanently,
|
@@ -275,7 +287,7 @@ module Fear
|
|
275
287
|
# m.left(String) { :err }
|
276
288
|
# m.else { 'error '}
|
277
289
|
# end
|
278
|
-
# matcher.call(
|
290
|
+
# matcher.call(Fear.right(42))
|
279
291
|
#
|
280
292
|
# @yieldparam [Fear::EitherPatternMatch]
|
281
293
|
# @return [Fear::PartialFunction]
|
@@ -292,16 +304,22 @@ module Fear
|
|
292
304
|
# Left('beaf') #=> #<Fear::Legt value='beaf'>
|
293
305
|
#
|
294
306
|
module Mixin
|
295
|
-
# @param [any]
|
296
|
-
# @return [Left]
|
307
|
+
# @param value [any]
|
308
|
+
# @return [Fear::Left]
|
309
|
+
# @example
|
310
|
+
# Left(42) #=> #<Fear::Left value=42>
|
311
|
+
#
|
297
312
|
def Left(value)
|
298
|
-
|
313
|
+
Fear.left(value)
|
299
314
|
end
|
300
315
|
|
301
|
-
# @param [any]
|
302
|
-
# @return [Right]
|
316
|
+
# @param value [any]
|
317
|
+
# @return [Fear::Right]
|
318
|
+
# @example
|
319
|
+
# Right(42) #=> #<Fear::Right value=42>
|
320
|
+
#
|
303
321
|
def Right(value)
|
304
|
-
|
322
|
+
Fear.right(value)
|
305
323
|
end
|
306
324
|
end
|
307
325
|
end
|