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.
Files changed (110) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +0 -1
  3. data/.rubocop.yml +18 -0
  4. data/.travis.yml +0 -3
  5. data/CHANGELOG.md +12 -1
  6. data/Gemfile +1 -0
  7. data/{gemfiles/dry_equalizer_0.2.1.gemfile.lock → Gemfile.lock} +21 -12
  8. data/README.md +594 -241
  9. data/Rakefile +166 -219
  10. data/benchmarks/README.md +1 -0
  11. data/benchmarks/dry_do_vs_fear_for.txt +11 -0
  12. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +11 -0
  13. data/benchmarks/factorial.txt +16 -0
  14. data/benchmarks/fear_gaurd_and1_vs_new.txt +13 -0
  15. data/benchmarks/fear_gaurd_and2_vs_and.txt +13 -0
  16. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +13 -0
  17. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +11 -0
  18. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +13 -0
  19. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +14 -0
  20. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +11 -0
  21. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +11 -0
  22. data/examples/pattern_extracting.rb +15 -0
  23. data/examples/pattern_matching_binary_tree_set.rb +96 -0
  24. data/examples/pattern_matching_number_in_words.rb +54 -0
  25. data/fear.gemspec +4 -2
  26. data/lib/fear.rb +21 -4
  27. data/lib/fear/either.rb +77 -59
  28. data/lib/fear/either_api.rb +21 -0
  29. data/lib/fear/empty_partial_function.rb +1 -1
  30. data/lib/fear/extractor.rb +108 -0
  31. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +8 -0
  32. data/lib/fear/extractor/any_matcher.rb +15 -0
  33. data/lib/fear/extractor/array_head_matcher.rb +34 -0
  34. data/lib/fear/extractor/array_matcher.rb +38 -0
  35. data/lib/fear/extractor/array_splat_matcher.rb +14 -0
  36. data/lib/fear/extractor/empty_list_matcher.rb +18 -0
  37. data/lib/fear/extractor/extractor_matcher.rb +42 -0
  38. data/lib/fear/extractor/grammar.rb +201 -0
  39. data/lib/fear/extractor/grammar.treetop +129 -0
  40. data/lib/fear/extractor/identifier_matcher.rb +16 -0
  41. data/lib/fear/extractor/matcher.rb +54 -0
  42. data/lib/fear/extractor/matcher/and.rb +36 -0
  43. data/lib/fear/extractor/named_array_splat_matcher.rb +15 -0
  44. data/lib/fear/extractor/pattern.rb +55 -0
  45. data/lib/fear/extractor/typed_identifier_matcher.rb +24 -0
  46. data/lib/fear/extractor/value_matcher.rb +17 -0
  47. data/lib/fear/extractor_api.rb +33 -0
  48. data/lib/fear/failure.rb +32 -10
  49. data/lib/fear/for.rb +14 -69
  50. data/lib/fear/for_api.rb +66 -0
  51. data/lib/fear/future.rb +414 -0
  52. data/lib/fear/future_api.rb +19 -0
  53. data/lib/fear/left.rb +8 -0
  54. data/lib/fear/none.rb +17 -8
  55. data/lib/fear/option.rb +55 -49
  56. data/lib/fear/option_api.rb +38 -0
  57. data/lib/fear/partial_function.rb +9 -12
  58. data/lib/fear/partial_function/empty.rb +1 -1
  59. data/lib/fear/partial_function/guard.rb +8 -20
  60. data/lib/fear/partial_function/lifted.rb +1 -0
  61. data/lib/fear/partial_function_class.rb +10 -0
  62. data/lib/fear/pattern_match.rb +10 -0
  63. data/lib/fear/pattern_matching_api.rb +35 -11
  64. data/lib/fear/promise.rb +87 -0
  65. data/lib/fear/right.rb +8 -0
  66. data/lib/fear/some.rb +22 -3
  67. data/lib/fear/success.rb +22 -1
  68. data/lib/fear/try.rb +82 -67
  69. data/lib/fear/try_api.rb +31 -0
  70. data/lib/fear/unit.rb +28 -0
  71. data/lib/fear/version.rb +1 -1
  72. data/spec/fear/done_spec.rb +3 -3
  73. data/spec/fear/either/mixin_spec.rb +15 -0
  74. data/spec/fear/either_pattern_match_spec.rb +10 -12
  75. data/spec/fear/extractor/array_matcher_spec.rb +228 -0
  76. data/spec/fear/extractor/extractor_matcher_spec.rb +151 -0
  77. data/spec/fear/extractor/grammar_array_spec.rb +23 -0
  78. data/spec/fear/extractor/identified_matcher_spec.rb +47 -0
  79. data/spec/fear/extractor/identifier_matcher_spec.rb +66 -0
  80. data/spec/fear/extractor/pattern_spec.rb +32 -0
  81. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +62 -0
  82. data/spec/fear/extractor/value_matcher_number_spec.rb +77 -0
  83. data/spec/fear/extractor/value_matcher_string_spec.rb +86 -0
  84. data/spec/fear/extractor/value_matcher_symbol_spec.rb +69 -0
  85. data/spec/fear/extractor_api_spec.rb +113 -0
  86. data/spec/fear/extractor_spec.rb +59 -0
  87. data/spec/fear/failure_spec.rb +73 -13
  88. data/spec/fear/for_spec.rb +35 -35
  89. data/spec/fear/future_spec.rb +466 -0
  90. data/spec/fear/guard_spec.rb +4 -4
  91. data/spec/fear/left_spec.rb +40 -14
  92. data/spec/fear/none_spec.rb +28 -12
  93. data/spec/fear/option/mixin_spec.rb +37 -0
  94. data/spec/fear/option_pattern_match_spec.rb +7 -9
  95. data/spec/fear/partial_function_spec.rb +25 -3
  96. data/spec/fear/pattern_match_spec.rb +33 -1
  97. data/spec/fear/promise_spec.rb +94 -0
  98. data/spec/fear/right_spec.rb +37 -9
  99. data/spec/fear/some_spec.rb +32 -6
  100. data/spec/fear/success_spec.rb +32 -4
  101. data/spec/fear/try/mixin_spec.rb +17 -0
  102. data/spec/fear/try_pattern_match_spec.rb +8 -10
  103. data/spec/spec_helper.rb +1 -1
  104. metadata +115 -20
  105. data/Appraisals +0 -32
  106. data/gemfiles/dry_equalizer_0.1.0.gemfile +0 -8
  107. data/gemfiles/dry_equalizer_0.1.0.gemfile.lock +0 -82
  108. data/gemfiles/dry_equalizer_0.2.1.gemfile +0 -8
  109. data/lib/fear/done.rb +0 -22
  110. data/spec/fear/option_spec.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f2a2b3d7163ba41625120e4681b75a845e5d9da9
4
- data.tar.gz: 5d85fb64e778587e9f8f85695acb4f19602ae47e
2
+ SHA256:
3
+ metadata.gz: b04f2a97dbf830e68f70c42b703e2e70f7baf389da09871b2024effe95f51c68
4
+ data.tar.gz: 7852518363fe60c10e9c60da873462e84e9b1ad567c760992bbf595b5eb388fb
5
5
  SHA512:
6
- metadata.gz: 99ff3b3886920d253e835b6d1346ff7680f26650a01087cbd83629ec3ba3e7a152ef7714937c653639eef1689dcbac242c7a18cfd26db8d41155f5f4cc9bd80b
7
- data.tar.gz: 7fcabd7b3cada123a3d2988c4cc4c19bf2af8195e10ff151a886f9a24e1b12dfa73061ca9bc323c056aee9f0be985886b74a02b23a2eeacc10d39e8da2735fb5
6
+ metadata.gz: 3b166826f4b02237d1614e334b98991271b8825d98cdb879d863abacdfc72587c0dfa47fe8762cab3b29cd7fa8472a5069092e1f8766e68807f8311bfe1c1614
7
+ data.tar.gz: 8393f1845627f280e8e65caf2f4f36b329106beef35445ef58d3a8d053b8f2fb6644060a0139256bb7e120d4005efcb1427debbec495c97205719614361c73ab
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
@@ -1,6 +1,17 @@
1
+ inherit_mode:
2
+ merge:
3
+ - Exclude
4
+
1
5
  AllCops:
2
6
  Exclude:
3
7
  - 'gemfiles/**/*'
8
+ - 'vendor/bundle/'
9
+
10
+ Layout/MultilineMethodCallIndentation:
11
+ EnforcedStyle: indented
12
+
13
+ Metrics/ClassLength:
14
+ Enabled: false
4
15
 
5
16
  Naming/MethodName:
6
17
  Enabled: false
@@ -35,11 +46,18 @@ Style/TrailingCommaInHashLiteral:
35
46
  Style/NumericPredicate:
36
47
  Enabled: false
37
48
 
49
+ Style/CommentedKeyword:
50
+ Enabled: false
51
+
52
+ Layout/IndentHeredoc:
53
+ Enabled: false
54
+
38
55
  Metrics/BlockLength:
39
56
  Exclude:
40
57
  - spec/**/*
41
58
  - Rakefile
42
59
  - fear.gemspec
60
+ - examples/**/*
43
61
 
44
62
  Metrics/LineLength:
45
63
  Max: 120
@@ -11,6 +11,3 @@ before_script:
11
11
  script:
12
12
  - bundle exec rspec
13
13
  - bundle exec rubocop --fail-level C
14
- gemfile:
15
- - gemfiles/dry_equalizer_0.2.1.gemfile
16
- - gemfiles/dry_equalizer_0.1.0.gemfile
@@ -1,6 +1,17 @@
1
+ ## 0.x
2
+
3
+ * Rename `Fear::Done` to `Fear::Unit` ([@bolshakov][])
4
+ * Don't treat symbols as procs while pattern matching. See [#46](https://github.com/bolshakov/fear/pull/46) for motivation ([@bolshakov][])
5
+ * Revert commit removing `Fear::Future`. Now you can use `Fear.future` again ([@bolshakov][])
6
+ * Signatures of `Try#recover` and `Try#recover_with` changed. No it pattern match against container
7
+ see https://github.com/bolshakov/fear/issues/41 for details . ([@bolshakov][])
8
+ * Add `#xcase` method to extract patterns ([@bolshakov][])
9
+ * Add `Fear.option`, `Fear.some`, `Fear.none`, `Fear.try`, `Fear.left`, `Fear.right`, and `Fear.for` alternatives to
10
+ including mixins. ([@bolshakov][])
11
+
1
12
  ## 0.11.0
2
13
 
3
- * Implement pattern matching and partial functions. See [README](https://github.com/bolshakov/fear#pattern-matching-api-documentation) (([@bolshakov][]))
14
+ * Implement pattern matching and partial functions. See [README](https://github.com/bolshakov/fear#pattern-matching-api-documentation) ([@bolshakov][])
4
15
  * `#to_a` method removed ([@bolshakov][])
5
16
  * `For` syntax changed. See [diff](https://github.com/bolshakov/fear/pull/22/files#diff-04c6e90faac2675aa89e2176d2eec7d8) ([@bolshakov][])
6
17
  * `Fear::None` is singleton object now and could not be instantiated ([@bolshakov][])
data/Gemfile CHANGED
@@ -5,4 +5,5 @@ gemspec
5
5
 
6
6
  # gem 'codeclimate-test-reporter', group: :test, require: nil
7
7
 
8
+ gem 'irb'
8
9
  gem 'qo', github: 'baweaver/qo'
@@ -6,28 +6,35 @@ GIT
6
6
  any (= 0.1.0)
7
7
 
8
8
  PATH
9
- remote: ..
9
+ remote: .
10
10
  specs:
11
- fear (0.11.0)
12
- dry-equalizer (<= 0.2.1)
11
+ fear (1.0.0)
12
+ lru_redux
13
+ treetop
13
14
 
14
15
  GEM
15
16
  remote: https://rubygems.org/
16
17
  specs:
17
18
  any (0.1.0)
18
- appraisal (2.2.0)
19
- bundler
20
- rake
21
- thor (>= 0.14.0)
22
19
  ast (2.4.0)
23
20
  benchmark-ips (2.7.2)
21
+ concurrent-ruby (1.1.5)
24
22
  diff-lcs (1.3)
25
- dry-equalizer (0.2.1)
23
+ dry-core (0.4.7)
24
+ concurrent-ruby (~> 1.0)
25
+ dry-equalizer (0.2.2)
26
26
  dry-matcher (0.7.0)
27
+ dry-monads (1.2.0)
28
+ concurrent-ruby (~> 1.0)
29
+ dry-core (~> 0.4, >= 0.4.4)
30
+ dry-equalizer
31
+ irb (1.0.0)
27
32
  jaro_winkler (1.5.2)
33
+ lru_redux (1.1.0)
28
34
  parallel (1.14.0)
29
35
  parser (2.6.0.0)
30
36
  ast (~> 2.4.0)
37
+ polyglot (0.3.5)
31
38
  powerpack (0.1.2)
32
39
  psych (3.1.0)
33
40
  rainbow (3.0.0)
@@ -57,7 +64,8 @@ GEM
57
64
  rubocop-rspec (1.32.0)
58
65
  rubocop (>= 0.60.0)
59
66
  ruby-progressbar (1.10.0)
60
- thor (0.20.3)
67
+ treetop (1.6.10)
68
+ polyglot (~> 0.3)
61
69
  unicode-display_width (1.4.1)
62
70
  yard (0.9.18)
63
71
 
@@ -65,12 +73,13 @@ PLATFORMS
65
73
  ruby
66
74
 
67
75
  DEPENDENCIES
68
- appraisal
69
76
  benchmark-ips
70
77
  bundler
71
- dry-equalizer (= 0.2.1)
78
+ concurrent-ruby
72
79
  dry-matcher
80
+ dry-monads
73
81
  fear!
82
+ irb
74
83
  qo!
75
84
  rake (~> 10.0)
76
85
  rspec (~> 3.1)
@@ -79,4 +88,4 @@ DEPENDENCIES
79
88
  yard
80
89
 
81
90
  BUNDLED WITH
82
- 1.16.2
91
+ 1.17.2
data/README.md CHANGED
@@ -26,10 +26,11 @@ Or install it yourself as:
26
26
  * [Option](#option-documentation)
27
27
  * [Try](#try-documentation)
28
28
  * [Either](#either-documentation)
29
+ * [Future](#future-documentation)
29
30
  * [For composition](#for-composition)
30
31
  * [Pattern Matching](#pattern-matching-api-documentation)
31
32
 
32
- ### Option ([Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Option))
33
+ ### Option ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/Option))
33
34
 
34
35
  Represents optional (nullable) values. Instances of `Option` are either an instance of
35
36
  `Some` or the object `None`.
@@ -37,7 +38,7 @@ Represents optional (nullable) values. Instances of `Option` are either an insta
37
38
  The most idiomatic way to use an `Option` instance is to treat it as a collection
38
39
 
39
40
  ```ruby
40
- name = Option(params[:name])
41
+ name = Fear.option(params[:name])
41
42
  upper = name.map(&:strip).select { |n| n.length != 0 }.map(&:upcase)
42
43
  puts upper.get_or_else('')
43
44
  ```
@@ -48,7 +49,7 @@ having to check for the existence of a value.
48
49
  A less-idiomatic way to use `Option` values is via pattern matching
49
50
 
50
51
  ```ruby
51
- Option(params[:name]).match do |m|
52
+ Fear.option(params[:name]).match do |m|
52
53
  m.some { |name| name.strip.upcase }
53
54
  m.none { 'No name value' }
54
55
  end
@@ -57,7 +58,7 @@ end
57
58
  or manually checking for non emptiness
58
59
 
59
60
  ```ruby
60
- name = Option(params[:name])
61
+ name = Fear.option(params[:name])
61
62
  if name.empty?
62
63
  puts 'No name value'
63
64
  else
@@ -65,16 +66,29 @@ else
65
66
  end
66
67
  ```
67
68
 
69
+ Alternatively, include `Fear::Option::Mixin` to use `Option()`, `Some()` and `None()` methods:
70
+
71
+ ```ruby
72
+ include Fear::Option::Mixin
73
+
74
+ Option(42) #=> #<Fear::Some get=42>
75
+ Option(nil) #=> #<Fear::None>
76
+
77
+ Some(42) #=> #<Fear::Some get=42>
78
+ Some(nil) #=> #<Fear::Some get=nil>
79
+ None() #=> #<Fear::None>
80
+ ```
81
+
68
82
  #### Option#get_or_else
69
83
 
70
84
  Returns the value from this `Some` or evaluates the given default argument if this is a `None`.
71
85
 
72
86
  ```ruby
73
- Some(42).get_or_else { 24/2 } #=> 42
74
- None.get_or_else { 24/2 } #=> 12
87
+ Fear.some(42).get_or_else { 24/2 } #=> 42
88
+ Fear.none.get_or_else { 24/2 } #=> 12
75
89
 
76
- Some(42).get_or_else(12) #=> 42
77
- None.get_or_else(12) #=> 12
90
+ Fear.some(42).get_or_else(12) #=> 42
91
+ Fear.none.get_or_else(12) #=> 12
78
92
  ```
79
93
 
80
94
  #### Option#or_else
@@ -82,9 +96,9 @@ None.get_or_else(12) #=> 12
82
96
  returns self `Some` or the given alternative if this is a `None`.
83
97
 
84
98
  ```ruby
85
- Some(42).or_else { Some(21) } #=> Some(42)
86
- None.or_else { Some(21) } #=> Some(21)
87
- None.or_else { None } #=> None
99
+ Fear.some(42).or_else { Fear.some(21) } #=> Fear.some(42)
100
+ Fear.none.or_else { Fear.some(21) } #=> Fear.some(21)
101
+ Fear.none.or_else { None } #=> None
88
102
  ```
89
103
 
90
104
  #### Option#inlude?
@@ -92,9 +106,9 @@ None.or_else { None } #=> None
92
106
  Checks if `Option` has an element that is equal (as determined by `==`) to given values.
93
107
 
94
108
  ```ruby
95
- Some(17).include?(17) #=> true
96
- Some(17).include?(7) #=> false
97
- None.include?(17) #=> false
109
+ Fear.some(17).include?(17) #=> true
110
+ Fear.some(17).include?(7) #=> false
111
+ Fear.none.include?(17) #=> false
98
112
  ```
99
113
 
100
114
  #### Option#each
@@ -102,8 +116,8 @@ None.include?(17) #=> false
102
116
  Performs the given block if this is a `Some`.
103
117
 
104
118
  ```ruby
105
- Some(17).each { |value| puts value } #=> prints 17
106
- None.each { |value| puts value } #=> does nothing
119
+ Fear.some(17).each { |value| puts value } #=> prints 17
120
+ Fear.none.each { |value| puts value } #=> does nothing
107
121
  ```
108
122
 
109
123
  #### Option#map
@@ -111,8 +125,8 @@ None.each { |value| puts value } #=> does nothing
111
125
  Maps the given block to the value from this `Some` or returns self if this is a `None`
112
126
 
113
127
  ```ruby
114
- Some(42).map { |v| v/2 } #=> Some(21)
115
- None.map { |v| v/2 } #=> None
128
+ Fear.some(42).map { |v| v/2 } #=> Fear.some(21)
129
+ Fear.none.map { |v| v/2 } #=> None
116
130
  ```
117
131
 
118
132
  #### Option#flat_map
@@ -120,8 +134,8 @@ None.map { |v| v/2 } #=> None
120
134
  Returns the given block applied to the value from this `Some` or returns self if this is a `None`
121
135
 
122
136
  ```ruby
123
- Some(42).flat_map { |v| Some(v/2) } #=> Some(21)
124
- None.flat_map { |v| Some(v/2) } #=> None
137
+ Fear.some(42).flat_map { |v| Fear.some(v/2) } #=> Fear.some(21)
138
+ Fear.none.flat_map { |v| Fear.some(v/2) } #=> None
125
139
  ```
126
140
 
127
141
  #### Option#any?
@@ -129,9 +143,9 @@ None.flat_map { |v| Some(v/2) } #=> None
129
143
  Returns `false` if `None` or returns the result of the application of the given predicate to the `Some` value.
130
144
 
131
145
  ```ruby
132
- Some(12).any?( |v| v > 10) #=> true
133
- Some(7).any?( |v| v > 10) #=> false
134
- None.any?( |v| v > 10) #=> false
146
+ Fear.some(12).any?( |v| v > 10) #=> true
147
+ Fear.some(7).any?( |v| v > 10) #=> false
148
+ Fear.none.any?( |v| v > 10) #=> false
135
149
  ```
136
150
 
137
151
  #### Option#select
@@ -140,9 +154,9 @@ Returns self if it is nonempty and applying the predicate to this `Option`'s val
140
154
  return `None`.
141
155
 
142
156
  ```ruby
143
- Some(42).select { |v| v > 40 } #=> Success(21)
144
- Some(42).select { |v| v < 40 } #=> None
145
- None.select { |v| v < 40 } #=> None
157
+ Fear.some(42).select { |v| v > 40 } #=> Fear.success(21)
158
+ Fear.some(42).select { |v| v < 40 } #=> None
159
+ Fear.none.select { |v| v < 40 } #=> None
146
160
  ```
147
161
 
148
162
  #### Option#reject
@@ -150,9 +164,9 @@ None.select { |v| v < 40 } #=> None
150
164
  Returns `Some` if applying the predicate to this `Option`'s value returns `false`. Otherwise, return `None`.
151
165
 
152
166
  ```ruby
153
- Some(42).reject { |v| v > 40 } #=> None
154
- Some(42).reject { |v| v < 40 } #=> Some(42)
155
- None.reject { |v| v < 40 } #=> None
167
+ Fear.some(42).reject { |v| v > 40 } #=> None
168
+ Fear.some(42).reject { |v| v < 40 } #=> Fear.some(42)
169
+ Fear.none.reject { |v| v < 40 } #=> None
156
170
  ```
157
171
 
158
172
  #### Option#get
@@ -164,14 +178,14 @@ Not an idiomatic way of using Option at all. Returns values of raise `NoSuchElem
164
178
  Returns `true` if the `Option` is `None`, `false` otherwise.
165
179
 
166
180
  ```ruby
167
- Some(42).empty? #=> false
168
- None.empty? #=> true
181
+ Fear.some(42).empty? #=> false
182
+ Fear.none.empty? #=> true
169
183
  ```
170
184
 
171
185
  @see https://github.com/scala/scala/blob/2.11.x/src/library/scala/Option.scala
172
186
 
173
187
 
174
- ### Try ([Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Try))
188
+ ### Try ([API Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Try))
175
189
 
176
190
  The `Try` represents a computation that may either result
177
191
  in an exception, or return a successfully computed value. Instances of `Try`,
@@ -183,10 +197,8 @@ exception-handling in all of the places that an exception
183
197
  might occur.
184
198
 
185
199
  ```ruby
186
- include Fear::Try::Mixin
187
-
188
- dividend = Try { Integer(params[:dividend]) }
189
- divisor = Try { Integer(params[:divisor]) }
200
+ dividend = Fear.try { Integer(params[:dividend]) }
201
+ divisor = Fear.try { Integer(params[:divisor]) }
190
202
  problem = dividend.flat_map { |x| divisor.map { |y| x / y } }
191
203
 
192
204
  problem.match |m|
@@ -218,13 +230,22 @@ type of default behavior in the case of failure.
218
230
  *NOTE*: Only non-fatal exceptions are caught by the combinators on `Try`.
219
231
  Serious system errors, on the other hand, will be thrown.
220
232
 
233
+ Alternatively, include `Fear::Try::Mixin` to use `Try()` method:
234
+
235
+ ```ruby
236
+ include Fear::Try::Mixin
237
+
238
+ Try { 4/0 } #=> #<Fear::Failure exception=...>
239
+ Try { 4/2 } #=> #<Fear::Success value=2>
240
+ ```
241
+
221
242
  #### Try#get_or_else
222
243
 
223
244
  Returns the value from this `Success` or evaluates the given default argument if this is a `Failure`.
224
245
 
225
246
  ```ruby
226
- Success(42).get_or_else { 24/2 } #=> 42
227
- Failure(ArgumentError.new).get_or_else { 24/2 } #=> 12
247
+ Fear.success(42).get_or_else { 24/2 } #=> 42
248
+ Fear.failure(ArgumentError.new).get_or_else { 24/2 } #=> 12
228
249
  ```
229
250
 
230
251
  #### Try#include?
@@ -232,9 +253,9 @@ Failure(ArgumentError.new).get_or_else { 24/2 } #=> 12
232
253
  Returns `true` if it has an element that is equal given values, `false` otherwise.
233
254
 
234
255
  ```ruby
235
- Success(17).include?(17) #=> true
236
- Success(17).include?(7) #=> false
237
- Failure(ArgumentError.new).include?(17) #=> false
256
+ Fear.success(17).include?(17) #=> true
257
+ Fear.success(17).include?(7) #=> false
258
+ Fear.failure(ArgumentError.new).include?(17) #=> false
238
259
  ```
239
260
 
240
261
  #### Try#each
@@ -243,8 +264,8 @@ Performs the given block if this is a `Success`. If block raise an error,
243
264
  then this method may raise an exception.
244
265
 
245
266
  ```ruby
246
- Success(17).each { |value| puts value } #=> prints 17
247
- Failure(ArgumentError.new).each { |value| puts value } #=> does nothing
267
+ Fear.success(17).each { |value| puts value } #=> prints 17
268
+ Fear.failure(ArgumentError.new).each { |value| puts value } #=> does nothing
248
269
  ```
249
270
 
250
271
  #### Try#map
@@ -252,8 +273,8 @@ Failure(ArgumentError.new).each { |value| puts value } #=> does nothing
252
273
  Maps the given block to the value from this `Success` or returns self if this is a `Failure`.
253
274
 
254
275
  ```ruby
255
- Success(42).map { |v| v/2 } #=> Success(21)
256
- Failure(ArgumentError.new).map { |v| v/2 } #=> Failure(ArgumentError.new)
276
+ Fear.success(42).map { |v| v/2 } #=> Fear.success(21)
277
+ Fear.failure(ArgumentError.new).map { |v| v/2 } #=> Fear.failure(ArgumentError.new)
257
278
  ```
258
279
 
259
280
  #### Try#flat_map
@@ -261,8 +282,8 @@ Failure(ArgumentError.new).map { |v| v/2 } #=> Failure(ArgumentError.new)
261
282
  Returns the given block applied to the value from this `Success`or returns self if this is a `Failure`.
262
283
 
263
284
  ```ruby
264
- Success(42).flat_map { |v| Success(v/2) } #=> Success(21)
265
- Failure(ArgumentError.new).flat_map { |v| Success(v/2) } #=> Failure(ArgumentError.new)
285
+ Fear.success(42).flat_map { |v| Fear.success(v/2) } #=> Fear.success(21)
286
+ Fear.failure(ArgumentError.new).flat_map { |v| Fear.success(v/2) } #=> Fear.failure(ArgumentError.new)
266
287
  ```
267
288
 
268
289
  #### Try#to_option
@@ -270,8 +291,8 @@ Failure(ArgumentError.new).flat_map { |v| Success(v/2) } #=> Failure(ArgumentErr
270
291
  Returns an `Some` containing the `Success` value or a `None` if this is a `Failure`.
271
292
 
272
293
  ```ruby
273
- Success(42).to_option #=> Some(21)
274
- Failure(ArgumentError.new).to_option #=> None
294
+ Fear.success(42).to_option #=> Fear.some(21)
295
+ Fear.failure(ArgumentError.new).to_option #=> None
275
296
  ```
276
297
 
277
298
  #### Try#any?
@@ -279,20 +300,20 @@ Failure(ArgumentError.new).to_option #=> None
279
300
  Returns `false` if `Failure` or returns the result of the application of the given predicate to the `Success` value.
280
301
 
281
302
  ```ruby
282
- Success(12).any?( |v| v > 10) #=> true
283
- Success(7).any?( |v| v > 10) #=> false
284
- Failure(ArgumentError.new).any?( |v| v > 10) #=> false
303
+ Fear.success(12).any?( |v| v > 10) #=> true
304
+ Fear.success(7).any?( |v| v > 10) #=> false
305
+ Fear.failure(ArgumentError.new).any?( |v| v > 10) #=> false
285
306
  ```
286
307
 
287
308
  #### Try#success? and Try#failure?
288
309
 
289
310
 
290
311
  ```ruby
291
- Success(12).success? #=> true
292
- Success(12).failure? #=> true
312
+ Fear.success(12).success? #=> true
313
+ Fear.success(12).failure? #=> true
293
314
 
294
- Failure(ArgumentError.new).success? #=> false
295
- Failure(ArgumentError.new).failure? #=> true
315
+ Fear.failure(ArgumentError.new).success? #=> false
316
+ Fear.failure(ArgumentError.new).failure? #=> true
296
317
  ```
297
318
 
298
319
  #### Try#get
@@ -300,8 +321,8 @@ Failure(ArgumentError.new).failure? #=> true
300
321
  Returns the value from this `Success` or raise the exception if this is a `Failure`.
301
322
 
302
323
  ```ruby
303
- Success(42).get #=> 42
304
- Failure(ArgumentError.new).get #=> ArgumentError: ArgumentError
324
+ Fear.success(42).get #=> 42
325
+ Fear.failure(ArgumentError.new).get #=> ArgumentError: ArgumentError
305
326
  ```
306
327
 
307
328
  #### Try#or_else
@@ -309,9 +330,9 @@ Failure(ArgumentError.new).get #=> ArgumentError: ArgumentError
309
330
  Returns self `Try` if it's a `Success` or the given alternative if this is a `Failure`.
310
331
 
311
332
  ```ruby
312
- Success(42).or_else { Success(-1) } #=> Success(42)
313
- Failure(ArgumentError.new).or_else { Success(-1) } #=> Success(-1)
314
- Failure(ArgumentError.new).or_else { Try { 1/0 } } #=> Failure(ZeroDivisionError.new('divided by 0'))
333
+ Fear.success(42).or_else { Fear.success(-1) } #=> Fear.success(42)
334
+ Fear.failure(ArgumentError.new).or_else { Fear.success(-1) } #=> Fear.success(-1)
335
+ Fear.failure(ArgumentError.new).or_else { Fear.try { 1/0 } } #=> Fear.failure(ZeroDivisionError.new('divided by 0'))
315
336
  ```
316
337
 
317
338
  #### Try#flatten
@@ -319,10 +340,10 @@ Failure(ArgumentError.new).or_else { Try { 1/0 } } #=> Failure(ZeroDivisionErro
319
340
  Transforms a nested `Try`, ie, a `Success` of `Success`, into an un-nested `Try`, ie, a `Success`.
320
341
 
321
342
  ```ruby
322
- Success(42).flatten #=> Success(42)
323
- Success(Success(42)).flatten #=> Success(42)
324
- Success(Failure(ArgumentError.new)).flatten #=> Failure(ArgumentError.new)
325
- Failure(ArgumentError.new).flatten { -1 } #=> Failure(ArgumentError.new)
343
+ Fear.success(42).flatten #=> Fear.success(42)
344
+ Fear.success(Fear.success(42)).flatten #=> Fear.success(42)
345
+ Fear.success(Fear.failure(ArgumentError.new)).flatten #=> Fear.failure(ArgumentError.new)
346
+ Fear.failure(ArgumentError.new).flatten { -1 } #=> Fear.failure(ArgumentError.new)
326
347
  ```
327
348
 
328
349
  #### Try#select
@@ -330,38 +351,58 @@ Failure(ArgumentError.new).flatten { -1 } #=> Failure(ArgumentError.new)
330
351
  Converts this to a `Failure` if the predicate is not satisfied.
331
352
 
332
353
  ```ruby
333
- Success(42).select { |v| v > 40 }
334
- #=> Success(21)
335
- Success(42).select { |v| v < 40 }
336
- #=> Failure(Fear::NoSuchElementError.new("Predicate does not hold for 42"))
337
- Failure(ArgumentError.new).select { |v| v < 40 }
338
- #=> Failure(ArgumentError.new)
354
+ Fear.success(42).select { |v| v > 40 }
355
+ #=> Fear.success(21)
356
+ Fear.success(42).select { |v| v < 40 }
357
+ #=> Fear.failure(Fear::NoSuchElementError.new("Predicate does not hold for 42"))
358
+ Fear.failure(ArgumentError.new).select { |v| v < 40 }
359
+ #=> Fear.failure(ArgumentError.new)
339
360
  ```
340
361
 
341
- #### Try#recover_with
362
+ #### Recovering from errors
342
363
 
343
- Applies the given block to exception. This is like `flat_map` for the exception.
364
+ There are two ways to recover from the error. `Try#recover_with` method is like `flat_map` for the exception. And
365
+ you can pattern match against the error!
344
366
 
345
367
  ```ruby
346
- Success(42).recover_with { |e| Success(e.massage) }
347
- #=> Success(42)
348
- Failure(ArgumentError.new).recover_with { |e| Success(e.massage) }
349
- #=> Success('ArgumentError')
350
- Failure(ArgumentError.new).recover_with { |e| raise }
351
- #=> Failure(RuntimeError)
368
+ Fear.success(42).recover_with do |m|
369
+ m.case(ZeroDivisionError) { Fear.success(0) }
370
+ end #=> Fear.success(42)
371
+
372
+ Fear.failure(ArgumentError.new).recover_with do |m|
373
+ m.case(ZeroDivisionError) { Fear.success(0) }
374
+ m.case(ArgumentError) { |error| Fear.success(error.class.name) }
375
+ end #=> Fear.success('ArgumentError')
352
376
  ```
353
377
 
354
- #### Try#recover
378
+ If the block raises error, this new error returned as an result
355
379
 
356
- Applies the given block to exception. This is like `map` for the exception.
380
+ ```ruby
381
+ Fear.failure(ArgumentError.new).recover_with do
382
+ raise
383
+ end #=> Fear.failure(RuntimeError)
384
+ ```
385
+
386
+ The second possibility for recovery is `Try#recover` method. It is like `map` for the exception. And it's also heavely
387
+ relies on pattern matching.
357
388
 
358
389
  ```ruby
359
- Success(42).recover { |e| e.massage }
360
- #=> Success(42)
361
- Failure(ArgumentError.new).recover { |e| e.massage }
362
- #=> Success('ArgumentError')
363
- Failure(ArgumentError.new).recover { |e| raise }
364
- #=> Failure(RuntimeError)
390
+ Fear.success(42).recover do |m|
391
+ m.case(&:message)
392
+ end #=> Fear.success(42)
393
+
394
+ Fear.failure(ArgumentError.new).recover do |m|
395
+ m.case(ZeroDivisionError) { 0 }
396
+ m.case(&:message)
397
+ end #=> Fear.success('ArgumentError')
398
+ ```
399
+
400
+ If the block raises an error, this new error returned as an result
401
+
402
+ ```ruby
403
+ Fear.failure(ArgumentError.new).recover do |m|
404
+ raise
405
+ end #=> Fear.failure(RuntimeError)
365
406
  ```
366
407
 
367
408
  #### Try#to_either
@@ -369,11 +410,11 @@ Failure(ArgumentError.new).recover { |e| raise }
369
410
  Returns `Left` with exception if this is a `Failure`, otherwise returns `Right` with `Success` value.
370
411
 
371
412
  ```ruby
372
- Success(42).to_either #=> Right(42)
373
- Failure(ArgumentError.new).to_either #=> Left(ArgumentError.new)
413
+ Fear.success(42).to_either #=> Fear.right(42)
414
+ Fear.failure(ArgumentError.new).to_either #=> Fear.left(ArgumentError.new)
374
415
  ```
375
416
 
376
- ### Either ([Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/Either))
417
+ ### Either ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/Option))
377
418
 
378
419
  Represents a value of one of two possible types (a disjoint union.)
379
420
  An instance of `Either` is either an instance of `Left` or `Right`.
@@ -390,9 +431,9 @@ received input is a +String+ or an +Fixnum+.
390
431
  ```ruby
391
432
  in = Readline.readline('Type Either a string or an Int: ', true)
392
433
  result = begin
393
- Right(Integer(in))
434
+ Fear.right(Integer(in))
394
435
  rescue ArgumentError
395
- Left(in)
436
+ Fear.left(in)
396
437
  end
397
438
 
398
439
  result.match do |m|
@@ -410,16 +451,25 @@ Either is right-biased, which means that `Right` is assumed to be the default ca
410
451
  operate on. If it is `Left`, operations like `#map`, `#flat_map`, ... return the `Left` value
411
452
  unchanged.
412
453
 
454
+ Alternatively, include `Fear::Either::Mixin` to use `Left()`, and `Right()` methods:
455
+
456
+ ```ruby
457
+ include Fear::Either::Mixin
458
+
459
+ Left(42) #=> #<Fear::Left value=42>
460
+ Right(42) #=> #<Fear::Right value=42>
461
+ ```
462
+
413
463
  #### Either#get_or_else
414
464
 
415
465
  Returns the value from this `Right` or evaluates the given default argument if this is a `Left`.
416
466
 
417
467
  ```ruby
418
- Right(42).get_or_else { 24/2 } #=> 42
419
- Left('undefined').get_or_else { 24/2 } #=> 12
468
+ Fear.right(42).get_or_else { 24/2 } #=> 42
469
+ Fear.left('undefined').get_or_else { 24/2 } #=> 12
420
470
 
421
- Right(42).get_or_else(12) #=> 42
422
- Left('undefined').get_or_else(12) #=> 12
471
+ Fear.right(42).get_or_else(12) #=> 42
472
+ Fear.left('undefined').get_or_else(12) #=> 12
423
473
  ```
424
474
 
425
475
  #### Either#or_else
@@ -427,9 +477,9 @@ Left('undefined').get_or_else(12) #=> 12
427
477
  Returns self `Right` or the given alternative if this is a `Left`.
428
478
 
429
479
  ```ruby
430
- Right(42).or_else { Right(21) } #=> Right(42)
431
- Left('unknown').or_else { Right(21) } #=> Right(21)
432
- Left('unknown').or_else { Left('empty') } #=> Left('empty')
480
+ Fear.right(42).or_else { Fear.right(21) } #=> Fear.right(42)
481
+ Fear.left('unknown').or_else { Fear.right(21) } #=> Fear.right(21)
482
+ Fear.left('unknown').or_else { Fear.left('empty') } #=> Fear.left('empty')
433
483
  ```
434
484
 
435
485
  #### Either#include?
@@ -437,9 +487,9 @@ Left('unknown').or_else { Left('empty') } #=> Left('empty')
437
487
  Returns `true` if `Right` has an element that is equal to given value, `false` otherwise.
438
488
 
439
489
  ```ruby
440
- Right(17).include?(17) #=> true
441
- Right(17).include?(7) #=> false
442
- Left('undefined').include?(17) #=> false
490
+ Fear.right(17).include?(17) #=> true
491
+ Fear.right(17).include?(7) #=> false
492
+ Fear.left('undefined').include?(17) #=> false
443
493
  ```
444
494
 
445
495
  #### Either#each
@@ -447,8 +497,8 @@ Left('undefined').include?(17) #=> false
447
497
  Performs the given block if this is a `Right`.
448
498
 
449
499
  ```ruby
450
- Right(17).each { |value| puts value } #=> prints 17
451
- Left('undefined').each { |value| puts value } #=> does nothing
500
+ Fear.right(17).each { |value| puts value } #=> prints 17
501
+ Fear.left('undefined').each { |value| puts value } #=> does nothing
452
502
  ```
453
503
 
454
504
  #### Either#map
@@ -456,8 +506,8 @@ Left('undefined').each { |value| puts value } #=> does nothing
456
506
  Maps the given block to the value from this `Right` or returns self if this is a `Left`.
457
507
 
458
508
  ```ruby
459
- Right(42).map { |v| v/2 } #=> Right(21)
460
- Left('undefined').map { |v| v/2 } #=> Left('undefined')
509
+ Fear.right(42).map { |v| v/2 } #=> Fear.right(21)
510
+ Fear.left('undefined').map { |v| v/2 } #=> Fear.left('undefined')
461
511
  ```
462
512
 
463
513
  #### Either#flat_map
@@ -465,8 +515,8 @@ Left('undefined').map { |v| v/2 } #=> Left('undefined')
465
515
  Returns the given block applied to the value from this `Right` or returns self if this is a `Left`.
466
516
 
467
517
  ```ruby
468
- Right(42).flat_map { |v| Right(v/2) } #=> Right(21)
469
- Left('undefined').flat_map { |v| Right(v/2) } #=> Left('undefined')
518
+ Fear.right(42).flat_map { |v| Fear.right(v/2) } #=> Fear.right(21)
519
+ Fear.left('undefined').flat_map { |v| Fear.right(v/2) } #=> Fear.left('undefined')
470
520
  ```
471
521
 
472
522
  #### Either#to_option
@@ -474,8 +524,8 @@ Left('undefined').flat_map { |v| Right(v/2) } #=> Left('undefined')
474
524
  Returns an `Some` containing the `Right` value or a `None` if this is a `Left`.
475
525
 
476
526
  ```ruby
477
- Right(42).to_option #=> Some(21)
478
- Left('undefined').to_option #=> None
527
+ Fear.right(42).to_option #=> Fear.some(21)
528
+ Fear.left('undefined').to_option #=> Fear::None
479
529
  ```
480
530
 
481
531
  #### Either#any?
@@ -483,9 +533,9 @@ Left('undefined').to_option #=> None
483
533
  Returns `false` if `Left` or returns the result of the application of the given predicate to the `Right` value.
484
534
 
485
535
  ```ruby
486
- Right(12).any?( |v| v > 10) #=> true
487
- Right(7).any?( |v| v > 10) #=> false
488
- Left('undefined').any?( |v| v > 10) #=> false
536
+ Fear.right(12).any?( |v| v > 10) #=> true
537
+ Fear.right(7).any?( |v| v > 10) #=> false
538
+ Fear.left('undefined').any?( |v| v > 10) #=> false
489
539
  ```
490
540
 
491
541
  #### Either#right?, Either#success?
@@ -493,8 +543,8 @@ Left('undefined').any?( |v| v > 10) #=> false
493
543
  Returns `true` if this is a `Right`, `false` otherwise.
494
544
 
495
545
  ```ruby
496
- Right(42).right? #=> true
497
- Left('err').right? #=> false
546
+ Fear.right(42).right? #=> true
547
+ Fear.left('err').right? #=> false
498
548
  ```
499
549
 
500
550
  #### Either#left?, Either#failure?
@@ -502,8 +552,8 @@ Left('err').right? #=> false
502
552
  Returns `true` if this is a `Left`, `false` otherwise.
503
553
 
504
554
  ```ruby
505
- Right(42).left? #=> false
506
- Left('err').left? #=> true
555
+ Fear.right(42).left? #=> false
556
+ Fear.left('err').left? #=> true
507
557
  ```
508
558
 
509
559
  #### Either#select_or_else
@@ -512,10 +562,10 @@ Returns `Left` of the default if the given predicate does not hold for the right
512
562
  returns `Right`.
513
563
 
514
564
  ```ruby
515
- Right(12).select_or_else(-1, &:even?) #=> Right(12)
516
- Right(7).select_or_else(-1, &:even?) #=> Left(-1)
517
- Left(12).select_or_else(-1, &:even?) #=> Left(12)
518
- Left(12).select_or_else(-> { -1 }, &:even?) #=> Left(12)
565
+ Fear.right(12).select_or_else(-1, &:even?) #=> Fear.right(12)
566
+ Fear.right(7).select_or_else(-1, &:even?) #=> Fear.left(-1)
567
+ Fear.left(12).select_or_else(-1, &:even?) #=> Fear.left(12)
568
+ Fear.left(12).select_or_else(-> { -1 }, &:even?) #=> Fear.left(12)
519
569
  ```
520
570
 
521
571
  #### Either#select
@@ -523,10 +573,10 @@ Left(12).select_or_else(-> { -1 }, &:even?) #=> Left(12)
523
573
  Returns `Left` of value if the given predicate does not hold for the right value, otherwise, returns `Right`.
524
574
 
525
575
  ```ruby
526
- Right(12).select(&:even?) #=> Right(12)
527
- Right(7).select(&:even?) #=> Left(7)
528
- Left(12).select(&:even?) #=> Left(12)
529
- Left(7).select(&:even?) #=> Left(7)
576
+ Fear.right(12).select(&:even?) #=> Fear.right(12)
577
+ Fear.right(7).select(&:even?) #=> Fear.left(7)
578
+ Fear.left(12).select(&:even?) #=> Fear.left(12)
579
+ Fear.left(7).select(&:even?) #=> Fear.left(7)
530
580
  ```
531
581
 
532
582
  #### Either#reject
@@ -534,10 +584,10 @@ Left(7).select(&:even?) #=> Left(7)
534
584
  Returns `Left` of value if the given predicate holds for the right value, otherwise, returns `Right`.
535
585
 
536
586
  ```ruby
537
- Right(12).reject(&:even?) #=> Left(12)
538
- Right(7).reject(&:even?) #=> Right(7)
539
- Left(12).reject(&:even?) #=> Left(12)
540
- Left(7).reject(&:even?) #=> Left(7)
587
+ Fear.right(12).reject(&:even?) #=> Fear.left(12)
588
+ Fear.right(7).reject(&:even?) #=> Fear.right(7)
589
+ Fear.left(12).reject(&:even?) #=> Fear.left(12)
590
+ Fear.left(7).reject(&:even?) #=> Fear.left(7)
541
591
  ```
542
592
 
543
593
  #### Either#swap
@@ -545,8 +595,8 @@ Left(7).reject(&:even?) #=> Left(7)
545
595
  If this is a `Left`, then return the left value in `Right` or vice versa.
546
596
 
547
597
  ```ruby
548
- Left('left').swap #=> Right('left')
549
- Right('right').swap #=> Light('left')
598
+ Fear.left('left').swap #=> Fear.right('left')
599
+ Fear.right('right').swap #=> Fear.left('left')
550
600
  ```
551
601
 
552
602
  #### Either#reduce
@@ -569,10 +619,10 @@ Joins an `Either` through `Right`. This method requires that the right side of t
569
619
  `Either` type. This method, and `join_left`, are analogous to `Option#flatten`
570
620
 
571
621
  ```ruby
572
- Right(Right(12)).join_right #=> Right(12)
573
- Right(Left("flower")).join_right #=> Left("flower")
574
- Left("flower").join_right #=> Left("flower")
575
- Left(Right("flower")).join_right #=> Left(Right("flower"))
622
+ Fear.right(Fear.right(12)).join_right #=> Fear.right(12)
623
+ Fear.right(Fear.left("flower")).join_right #=> Fear.left("flower")
624
+ Fear.left("flower").join_right #=> Fear.left("flower")
625
+ Fear.left(Fear.right("flower")).join_right #=> Fear.left(Fear.right("flower"))
576
626
  ```
577
627
 
578
628
  #### Either#join_right
@@ -581,32 +631,179 @@ Joins an `Either` through `Left`. This method requires that the left side of thi
581
631
  `Either` type. This method, and `join_right`, are analogous to `Option#flatten`
582
632
 
583
633
  ```ruby
584
- Left(Right("flower")).join_left #=> Right("flower")
585
- Left(Left(12)).join_left #=> Left(12)
586
- Right("daisy").join_left #=> Right("daisy")
587
- Right(Left("daisy")).join_left #=> Right(Left("daisy"))
634
+ Fear.left(Fear.right("flower")).join_left #=> Fear.right("flower")
635
+ Fear.left(Fear.left(12)).join_left #=> Fear.left(12)
636
+ Fear.right("daisy").join_left #=> Fear.right("daisy")
637
+ Fear.right(Fear.left("daisy")).join_left #=> Fear.right(Fear.left("daisy"))
588
638
  ```
589
-
590
- ### For composition
639
+
640
+ ### Future ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/Future))
641
+
642
+ Asynchronous computations that yield futures are created
643
+ with the `Fear.future` call
644
+
645
+ ```ruby
646
+ success = "Hello"
647
+ f = Fear.future { success + ' future!' }
648
+ f.on_success do |result|
649
+ puts result
650
+ end
651
+ ```
652
+
653
+ Multiple callbacks may be registered; there is no guarantee
654
+ that they will be executed in a particular order.
655
+
656
+ The future may contain an exception and this means
657
+ that the future failed. Futures obtained through combinators
658
+ have the same error as the future they were obtained from.
659
+
660
+ ```ruby
661
+ f = Fear.future { 5 }
662
+ g = Fear.future { 3 }
663
+
664
+ f.flat_map do |x|
665
+ g.map { |y| x + y }
666
+ end
667
+ ```
668
+
669
+ Futures use [Concurrent::Promise](https://ruby-concurrency.github.io/concurrent-ruby/1.1.5/Concurrent/Promise.html#constructor_details)
670
+ under the hood. `Fear.future` accepts optional configuration Hash passed directly to underlying promise. For example,
671
+ run it on custom thread pool.
672
+
673
+ ```ruby
674
+ require 'open-uri'
675
+ pool = Concurrent::FixedThreadPool.new(5)
676
+ future = Fear.future(executor: pool) { open('https://example.com/') }
677
+ future.map(&:read).each do |body|
678
+ puts "#{body}"
679
+ end
680
+
681
+ ```
682
+
683
+ Futures support common monadic operations -- `#map`, `#flat_map`, and `#each`. That's why it's possible to combine them
684
+ using `Fear.for`, It returns the Future containing Success of `5 + 3` eventually.
685
+
686
+ ```ruby
687
+ f = Fear.future { 5 }
688
+ g = Fear.future { 3 }
689
+
690
+ Fear.for(f, g) do |x, y|
691
+ x + y
692
+ end
693
+ ```
694
+
695
+ Future goes with the number of callbacks. You can register several callbacks, but the order of execution isn't guaranteed
696
+
697
+ ```ruby
698
+ f = Fear.future { ... } # call external service
699
+ f.on_success do |result|
700
+ # handle service response
701
+ end
702
+
703
+ f.on_failure do |error|
704
+ # handle exception
705
+ end
706
+ ```
707
+
708
+ or you can wait for Future completion
709
+
710
+ ```ruby
711
+ f.on_complete do |result|
712
+ result.match do |m|
713
+ m.success { |value| ... }
714
+ m.failure { |error| ... }
715
+ end
716
+ end
717
+ ```
718
+
719
+ In sake of convenience `#on_success` callback aliased as `#each`.
720
+
721
+ It's possible to get future value directly, but since it may be incomplete, `#value` method returns `Fear::Option`. So,
722
+ there are three possible responses:
723
+
724
+ ```ruby
725
+ future.value #=>
726
+ # Fear::Some<Fear::Success> #=> future completed with value
727
+ # Fear::Some<Fear::Failure> #=> future completed with error
728
+ # Fear::None #=> future not yet completed
729
+ ```
730
+
731
+ There is a variety of methods to manipulate with futures.
732
+
733
+ ```ruby
734
+ Fear.future { open('http://example.com').read }
735
+ .transform(
736
+ ->(value) { ... },
737
+ ->(error) { ... },
738
+ )
739
+
740
+ future = Fear.future { 5 }
741
+ future.select(&:odd?) # evaluates to Fear.success(5)
742
+ future.select(&:even?) # evaluates to Fear.error(NoSuchElementError)
743
+ ```
744
+
745
+ You can zip several asynchronous computations into one future. For you can call two external services and
746
+ then zip the results into one future containing array of both responses:
747
+
748
+ ```ruby
749
+ future1 = Fear.future { call_service1 }
750
+ future1 = Fear.future { call_service2 }
751
+ future1.zip(future2)
752
+ ```
753
+
754
+ It returns the same result as `Fear.future { [call_service1, call_service2] }`, but the first version performs
755
+ two simultaneous calls.
756
+
757
+ There are two ways to recover from failure. `Future#recover` is live `#map` for failures:
758
+
759
+ ```ruby
760
+ Fear.future { 2 / 0 }.recover do |m|
761
+ m.case(ZeroDivisionError) { 0 }
762
+ end #=> returns new future of Fear.success(0)
763
+ ```
764
+
765
+ If the future resolved to success or recovery matcher did not matched, it returns the future `Fear::Failure`.
766
+
767
+ The second option is `Future#fallbock_to` method. It allows to fallback to result of another future in case of failure
768
+
769
+ ```ruby
770
+ future = Fear.future { fail 'error' }
771
+ fallback = Fear.future { 5 }
772
+ future.fallback_to(fallback) # evaluates to 5
773
+ ```
774
+
775
+ You can run callbacks in specific order using `#and_then` method:
776
+
777
+ ```ruby
778
+ f = Fear.future { 5 }
779
+ f.and_then do
780
+ fail 'runtime error'
781
+ end.and_then do |m|
782
+ m.success { |value| puts value } # it evaluates this branch
783
+ m.failure { |error| puts error.massage }
784
+ end
785
+ ```
786
+
787
+ ### For composition ([API Documentation](http://www.rubydoc.info/github/bolshakov/fear/master/Fear/ForApi))
591
788
 
592
789
  Provides syntactic sugar for composition of multiple monadic operations.
593
790
  It supports two such operations - `flat_map` and `map`. Any class providing them
594
791
  is supported by `For`.
595
792
 
596
793
  ```ruby
597
- For(Some(2), Some(3)) do |a, b|
794
+ Fear.for(Fear.some(2), Fear.some(3)) do |a, b|
598
795
  a * b
599
- end #=> Some(6)
796
+ end #=> Fear.some(6)
600
797
  ```
601
798
 
602
799
  If one of operands is None, the result is None
603
800
 
604
801
  ```ruby
605
- For(Some(2), None) do |a, b|
802
+ Fear.for(Fear.some(2), None) do |a, b|
606
803
  a * b
607
804
  end #=> None
608
805
 
609
- For(None, Some(2)) do |a, b|
806
+ Fear.for(None, Fear.some(2)) do |a, b|
610
807
  a * b
611
808
  end #=> None
612
809
  ```
@@ -614,7 +811,7 @@ end #=> None
614
811
  Lets look at first example:
615
812
 
616
813
  ```ruby
617
- For(Some(2), None) do |a, b|
814
+ Fear.for(Fear.some(2), None) do |a, b|
618
815
  a * b
619
816
  end #=> None
620
817
  ```
@@ -622,8 +819,8 @@ end #=> None
622
819
  it is translated to:
623
820
 
624
821
  ```ruby
625
- Some(2).flat_map do |a|
626
- Some(3).map do |b|
822
+ Fear.some(2).flat_map do |a|
823
+ Fear.some(3).map do |b|
627
824
  a * b
628
825
  end
629
826
  end
@@ -632,7 +829,7 @@ end
632
829
  It works with arrays as well
633
830
 
634
831
  ```ruby
635
- For([1, 2], [2, 3], [3, 4]) { |a, b, c| a * b * c }
832
+ Fear.for([1, 2], [2, 3], [3, 4]) { |a, b, c| a * b * c }
636
833
  #=> [6, 8, 9, 12, 12, 16, 18, 24]
637
834
 
638
835
  ```
@@ -653,7 +850,7 @@ If you pass lambda as a variable value, it would be evaluated
653
850
  only on demand.
654
851
 
655
852
  ```ruby
656
- For(proc { None }, proc { raise 'kaboom' } ) do |a, b|
853
+ Fear.for(proc { None }, proc { raise 'kaboom' } ) do |a, b|
657
854
  a * b
658
855
  end #=> None
659
856
  ```
@@ -664,131 +861,222 @@ You can refer to previously defined variables from within lambdas.
664
861
  ```ruby
665
862
  maybe_user = find_user('Paul') #=> <#Option value=<#User ...>>
666
863
 
667
- For(maybe_user, ->(user) { user.birthday }) do |user, birthday|
864
+ Fear.for(maybe_user, ->(user) { user.birthday }) do |user, birthday|
668
865
  "#{user.name} was born on #{birthday}"
669
- end #=> Some('Paul was born on 1987-06-17')
866
+ end #=> Fear.some('Paul was born on 1987-06-17')
670
867
  ```
671
868
 
672
- ### Pattern Matching (API Documentation)
869
+ ### Pattern Matching ([API Documentation](https://www.rubydoc.info/github/bolshakov/fear/master/Fear/PatternMatchingApi))
673
870
 
674
- Pattern matcher is a combination of partial functions wrapped into nice DSL. Every partial function
675
- defined on domain described with guard.
871
+ #### Syntax
676
872
 
677
- ```ruby
678
- pf = Fear.case(Integer) { |x| x / 2 }
679
- pf.defined_at?(4) #=> true
680
- pf.defined_at?('Foo') #=> false
681
- pf.call('Foo') #=> raises Fear::MatchError
682
- pf.call_or_else('Foo') { 'not a number' } #=> 'not a number'
683
- pf.call_or_else(4) { 'not a number' } #=> 2
684
- pf.lift.call('Foo') #=> Fear::None
685
- pf.lift.call(4) #=> Fear::Some(2)
873
+ To pattern match against a value, use `Fear.match` function, and provide at least one case clause:
874
+
875
+ ```ruby
876
+ x = Random.rand(10)
877
+
878
+ Fear.match(x) do |m|
879
+ m.case(0) { 'zero' }
880
+ m.case(1) { 'one' }
881
+ m.case(2) { 'two' }
882
+ m.else { 'many' }
883
+ end
686
884
  ```
687
885
 
688
- It uses `#===` method under the hood, so you can pass:
886
+ The `x` above is a random integer from 0 to 10. The last clause `else` is a “catch all” case
887
+ for anything other than `0`, `1`, and `2`. If you want to ensure that an Integer value is passed,
888
+ matching against type available:
689
889
 
690
- * Class to check kind of an object.
691
- * Lambda to evaluate it against an object.
692
- * Any literal, like `4`, `"Foobar"`, etc.
693
- * Symbol -- it is converted to lambda using `#to_proc` method.
694
- * Qo matcher -- `m.case(Qo[name: 'John']) { .... }`
695
-
696
- Partial functions may be combined with each other:
890
+ ```ruby
891
+ Fear.match(x) do |m|
892
+ m.case(Integer, 0) { 'zero' }
893
+ m.case(Integer, 1) { 'one' }
894
+ m.case(Integer, 2) { 'two' }
895
+ m.case(Integer) { 'many' }
896
+ end
897
+ ```
697
898
 
698
- ```ruby
699
- is_even = Fear.case(->(arg) { arg % 2 == 0}) { |arg| "#{arg} is even" }
700
- is_odd = Fear.case(->(arg) { arg % 2 == 1}) { |arg| "#{arg} is odd" }
899
+ Providing something other than Integer will raise `Fear::MatchError` error.
701
900
 
702
- (10..20).map(&is_even.or_else(is_odd))
901
+ #### Pattern guards
703
902
 
704
- to_integer = Fear.case(String, &:to_i)
705
- integer_two_times = Fear.case(Integer) { |x| x * 2 }
903
+ You can use whatever you want as a pattern guard, if it respond to `#===` method to to make cases more specific.
706
904
 
707
- two_times = to_integer.and_then(integer_two_times).or_else(integer_two_times)
708
- two_times.(4) #=> 8
709
- two_times.('42') #=> 84
905
+ ```ruby
906
+ m.case(20..40) { |m| "#{m} is within range" }
907
+ m.case(->(x) { x > 10}) { |m| "#{m} is greater than 10" }
908
+ m.case(:even?.to_proc) { |x| "#{x} is even" }
909
+ m.case(:odd?.to_proc) { |x| "#{x} is odd" }
710
910
  ```
711
911
 
712
- To create custom pattern match use `Fear.match` method and `case` builder to define
713
- branches. For instance this matcher applies different functions to Integers and Strings
912
+ It's also possible to create matcher and use it several times:
714
913
 
715
- ```ruby
716
- Fear.match(value) do |m|
914
+ ```ruby
915
+ matcher = Fear.matcher do |m|
717
916
  m.case(Integer) { |n| "#{n} is a number" }
718
917
  m.case(String) { |n| "#{n} is a string" }
918
+ m.else { |n| "#{n} is a #{n.class}" }
919
+ end
920
+
921
+ matcher.(42) #=> "42 is a number"
922
+ matcher.(10..20) #=> "10..20 is a Range"
923
+ ```
924
+
925
+ #### Pattern extraction
926
+
927
+ It's possible to use special syntax to match against an object and extract a variable form this object.
928
+ To perform such extraction, `#xcase` method should be used. The following example should give you a sense
929
+ how extraction works.
930
+
931
+ ```ruby
932
+ matcher = Fear.matcher do |m|
933
+ m.xcase('[1, *tail]') { |tail:| tail }
719
934
  end
720
935
  ```
721
936
 
722
- if you pass something other than Integer or string, it will raise `Fear::MatchError` error.
723
- To avoid raising `MatchError`, you can use `else` method. It defines a branch matching
724
- on any value.
937
+ It matches only on an array starting from `1` integer, and captures its tail:
938
+
939
+ ```ruby
940
+ matcher.([1,2,3]) #=> [2,3]
941
+ matcher.([2,3]) #=> raises MatchError
942
+ ```
943
+
944
+ If you want to match against any value, use `_`
725
945
 
726
946
  ```ruby
727
- Fear.match(10..20) do |m|
728
- m.case(Integer) { |n| "#{n} is a number" }
729
- m.case(String) { |n| "#{n} is a string" }
730
- m.else { |n| "#{n} is a #{n.class}" }
731
- end #=> "10..20 is a Range"
947
+ matcher = Fear.matcher do |m|
948
+ m.xcase('[1, _, 3]') { .. }
949
+ end
732
950
  ```
733
951
 
734
- You can use anything as a guardian if it responds to `#===` method:
952
+ It matches against `[1, 2, 3]`, `[1, 'foo', 3]`, but not `[1, 2]`. It's also possible to capture several variables
953
+ at the same time. Tho following example describes an array starting from `1`, and captures second and third elements.
954
+
955
+
956
+ ```ruby
957
+ matcher = Fear.matcher do |m|
958
+ m.xcase('[1, second, third]') { |second:, third: |.. }
959
+ end
960
+ ```
961
+
962
+ Matching on deeper structures is possible as well:
963
+
964
+ ```ruby
965
+ matcher = Fear.matcher do |m|
966
+ m.xcase('[["status", first_status], 4, *tail]') { |first_status:, tail: |.. }
967
+ end
968
+ ```
969
+
970
+ If you want to capture variable of specific type, there is a type matcher for that case:
971
+
972
+ ```ruby
973
+ matcher = Fear.matcher do |m|
974
+ m.xcase('[head : String, 2, *]') { |head: | head }
975
+ end
976
+ matcher.(['foo', 2]) #=> 'foo'
977
+ matcher.(['foo', 3]) #=> MatchError
978
+ matcher.([1, 2]) #=> MatchError
979
+ ```
980
+
981
+ You can extract variables from more complex objects. Fear packed with extractors for monads and `Date` object:
735
982
 
736
983
  ```ruby
737
- m.case(20..40) { |m| "#{m} is within range" }
738
- m.case(->(x) { x > 10}) { |m| "#{m} is greater than 10" }
984
+ Fear.matcher do |m|
985
+ m.xcase('Date(year, 2, 29)', ->(year:) { year < 2000 }) do |year:|
986
+ "#{year} is a leap year before Millennium"
987
+ end
988
+
989
+ m.xcase('Date(year, 2, 29)') do |year:|
990
+ "#{year} is a leap year after Millennium"
991
+ end
992
+
993
+ m.case(Date) do |date|
994
+ "#{date.year} is not a leap year"
995
+ end
996
+ end
997
+ ```
998
+
999
+ This matcher extracts values from date object and match against them at the same time
1000
+
1001
+ ```ruby
1002
+ matcher.(Date.new(1996,02,29)) #=> "1996 is a leap year before Millennium"
1003
+ matcher.(Date.new(2004,02,29)) #=> "1996 is a leap year after Millennium"
1004
+ matcher.(Date.new(2003,01,24)) #=> "2003 is not a leap year"
739
1005
  ```
740
1006
 
741
- If you pass a Symbol, it will be converted to proc using `#to_proc` method
1007
+ Nothing tricky here. The extractor object takes an object and tries to give back the arguments. It's like
1008
+ constructor, but instead of construction an object, it deconstructs it.
1009
+
1010
+ An argument of an extractor may be also a pattern or even introduce a new variable.
742
1011
 
743
1012
  ```ruby
744
- m.case(:even?) { |x| "#{x} is even" }
745
- m.case(:odd?) { |x| "#{x} is odd" }
1013
+ matcher = Fear.matcher do |m|
1014
+ m.xcase('Some([status : Integer, body : String])') do |status:, body:|
1015
+ "#{body.bytesize} bytes received with code #{status}"
1016
+ end
1017
+ end
1018
+
1019
+ matcher.(Fear.some([200, 'hello'])) #=> "5 bytes received with code 200"
1020
+ matcher.(Fear.some(['hello', 200])) #=> MatchError
746
1021
  ```
747
1022
 
748
- It's also possible to pass several guardians. All should match to pass
1023
+ You can provide extractors for you own classes
749
1024
 
750
1025
  ```ruby
751
- m.case(Integer, :even?) { |x| ... }
752
- m.case(Integer, :odd?) { |x| ... }
1026
+ Fear.register_extractor(User, Fear.case(User) { |user| [user.id, user.email] }.lift)
1027
+ # is the same as
1028
+ Fear.register_extractor(User, proc do |user|
1029
+ if user.is_a?(User)
1030
+ Fear.some([user.id, user.email])
1031
+ else
1032
+ Fear.none
1033
+ end
1034
+ end)
753
1035
  ```
754
1036
 
755
- It's also possible to create matcher and use it several times:
1037
+ Now extracting user's id and email is possible:
756
1038
 
757
- ```ruby
758
- matcher = Fear.matcher do |m|
759
- m.case(Integer) { |n| "#{n} is a number" }
760
- m.case(String) { |n| "#{n} is a string" }
761
- m.else { |n| "#{n} is a #{n.class}" }
762
- end
763
1039
 
764
- matcher.(42) #=> "42 is a number"
765
- matcher.(10..20) #=> "10..20 is a Range"
766
- ```
1040
+ ```ruby
1041
+ Fear.match(user) do |m|
1042
+ m.xcase('User(id, email)') { |id:, email:| }
1043
+ end
1044
+ ```
767
1045
 
768
- Since matcher is just a syntactic sugar for partial functions, you can combine matchers with partial
769
- functions and each other.
1046
+ Note, registered extractor should return either array of arguments, or boolean.
1047
+
1048
+ #### Extracting struct
1049
+
1050
+ There is predefined `Struct` extractor:
770
1051
 
771
1052
  ```ruby
772
- handle_numbers = Fear.case(Integer, &:itself).and_then(
773
- Fear.matcher do |m|
774
- m.case(0) { 'zero' }
775
- m.case(->(n) { n < 10 }) { 'smaller than ten' }
776
- m.case(->(n) { n > 10 }) { 'bigger than ten' }
777
- end
778
- )
1053
+ Envelope = Struct.new(:id, :receiver, :sender, :message)
779
1054
 
780
- handle_strings = Fear.case(String, &:itself).and_then(
781
- Fear.matcher do |m|
782
- m.case('zero') { 0 }
783
- m.case('one') { 1 }
784
- m.else { 'unexpected' }
1055
+ Fear.matcher do |m|
1056
+ m.xcase('envelope @ Envelope(id, _, sender, _)') do |id:, sender:, envelope:|
1057
+ acknowledge(id, sender)
1058
+ process(acknowledge)
785
1059
  end
786
- )
1060
+ end
1061
+ ```
787
1062
 
788
- handle = handle_numbers.or_else(handle_strings)
789
- handle.(0) #=> 'zero'
790
- handle.(12) #=> 'bigger than ten'
791
- handle.('one') #=> 1
1063
+ #### How to debug pattern extractors?
1064
+
1065
+ You can build pattern manually and ask for failure reason:
1066
+
1067
+ ```ruby
1068
+ Fear['Some([:err, 444])'].failure_reason(Fear.some([:err, 445]))
1069
+ # =>
1070
+ Expected `445` to match:
1071
+ Some([:err, 444])
1072
+ ~~~~~~~~~~~~^
1073
+ ```
1074
+
1075
+ by the way you can also match against such pattern
1076
+
1077
+ ```ruby
1078
+ Fear['Some([:err, 444])'] === Fear.some([:err, 445]) #=> false
1079
+ Fear['Some([:err, 444])'] === Fear.some([:err, 445]) #=> true
792
1080
  ```
793
1081
 
794
1082
  #### More examples
@@ -811,7 +1099,6 @@ fibonnaci = Fear.matcher do |m|
811
1099
  m.case(0) { 0 }
812
1100
  m.case(1) { 1 }
813
1101
  m.case(->(n) { n > 1}) { |n| fibonnaci.(n - 1) + fibonnaci.(n - 2) }
814
- m.else { raise 'should be positive' }
815
1102
  end
816
1103
 
817
1104
  fibonnaci.(10) #=> 55
@@ -827,7 +1114,7 @@ only on container itself, but on enclosed value as well.
827
1114
  Pattern match against an `Option`
828
1115
 
829
1116
  ```ruby
830
- Some(42).match do |m|
1117
+ Fear.some(42).match do |m|
831
1118
  m.some { |x| x * 2 }
832
1119
  m.none { 'none' }
833
1120
  end #=> 84
@@ -836,9 +1123,9 @@ end #=> 84
836
1123
  pattern match on enclosed value
837
1124
 
838
1125
  ```ruby
839
- Some(41).match do |m|
840
- m.some(:even?) { |x| x / 2 }
841
- m.some(:odd?, ->(v) { v > 0 }) { |x| x * 2 }
1126
+ Fear.some(41).match do |m|
1127
+ m.some(:even?.to_proc) { |x| x / 2 }
1128
+ m.some(:odd?.to_proc, ->(v) { v > 0 }) { |x| x * 2 }
842
1129
  m.none { 'none' }
843
1130
  end #=> 82
844
1131
  ```
@@ -846,8 +1133,8 @@ end #=> 82
846
1133
  it raises `Fear::MatchError` error if nothing matched. To avoid exception, you can pass `#else` branch
847
1134
 
848
1135
  ```ruby
849
- Some(42).match do |m|
850
- m.some(:odd?) { |x| x * 2 }
1136
+ Fear.some(42).match do |m|
1137
+ m.some(:odd?.to_proc) { |x| x * 2 }
851
1138
  m.else { 'nothing' }
852
1139
  end #=> nothing
853
1140
  ```
@@ -857,16 +1144,81 @@ Pattern matching works the similar way for `Either` and `Try` monads.
857
1144
  In sake of performance, you may want to generate pattern matching function and reuse it multiple times:
858
1145
 
859
1146
  ```ruby
860
- matcher = Option.matcher do |m|
1147
+ matcher = Fear::Option.matcher do |m|
861
1148
  m.some(42) { 'Yep' }
862
1149
  m.some { 'Nope' }
863
1150
  m.none { 'Error' }
864
1151
  end
865
1152
 
866
- matcher.(Some(42)) #=> 'Yep'
867
- matcher.(Some(40)) #=> 'Nope'
1153
+ matcher.(Fear.some(42)) #=> 'Yep'
1154
+ matcher.(Fear.some(40)) #=> 'Nope'
868
1155
  ```
869
1156
 
1157
+ #### Under the hood
1158
+
1159
+ Pattern matcher is a combination of partial functions wrapped into nice DSL. Every partial function
1160
+ defined on domain described with guard.
1161
+
1162
+ ```ruby
1163
+ pf = Fear.case(Integer) { |x| x / 2 }
1164
+ pf.defined_at?(4) #=> true
1165
+ pf.defined_at?('Foo') #=> false
1166
+ pf.call('Foo') #=> raises Fear::MatchError
1167
+ pf.call_or_else('Foo') { 'not a number' } #=> 'not a number'
1168
+ pf.call_or_else(4) { 'not a number' } #=> 2
1169
+ pf.lift.call('Foo') #=> Fear::None
1170
+ pf.lift.call(4) #=> Fear.some(2)
1171
+ ```
1172
+
1173
+ It uses `#===` method under the hood, so you can pass:
1174
+
1175
+ * Class to check kind of an object.
1176
+ * Lambda to evaluate it against an object.
1177
+ * Any literal, like `4`, `"Foobar"`, `:not_found` etc.
1178
+ * Qo matcher -- `m.case(Qo[name: 'John']) { .... }`
1179
+
1180
+ Partial functions may be combined with each other:
1181
+
1182
+ ```ruby
1183
+ is_even = Fear.case(->(arg) { arg % 2 == 0}) { |arg| "#{arg} is even" }
1184
+ is_odd = Fear.case(->(arg) { arg % 2 == 1}) { |arg| "#{arg} is odd" }
1185
+
1186
+ (10..20).map(&is_even.or_else(is_odd))
1187
+
1188
+ to_integer = Fear.case(String, &:to_i)
1189
+ integer_two_times = Fear.case(Integer) { |x| x * 2 }
1190
+
1191
+ two_times = to_integer.and_then(integer_two_times).or_else(integer_two_times)
1192
+ two_times.(4) #=> 8
1193
+ two_times.('42') #=> 84
1194
+ ```
1195
+
1196
+ Since matcher is just a syntactic sugar for partial functions, you can combine matchers with partial
1197
+ functions and each other.
1198
+
1199
+ ```ruby
1200
+ handle_numbers = Fear.case(Integer, &:itself).and_then(
1201
+ Fear.matcher do |m|
1202
+ m.case(0) { 'zero' }
1203
+ m.case(->(n) { n < 10 }) { 'smaller than ten' }
1204
+ m.case(->(n) { n > 10 }) { 'bigger than ten' }
1205
+ end
1206
+ )
1207
+
1208
+ handle_strings = Fear.case(String, &:itself).and_then(
1209
+ Fear.matcher do |m|
1210
+ m.case('zero') { 0 }
1211
+ m.case('one') { 1 }
1212
+ m.else { 'unexpected' }
1213
+ end
1214
+ )
1215
+
1216
+ handle = handle_numbers.or_else(handle_strings)
1217
+ handle.(0) #=> 'zero'
1218
+ handle.(12) #=> 'bigger than ten'
1219
+ handle.('one') #=> 1
1220
+ ```
1221
+
870
1222
  ## Testing
871
1223
 
872
1224
  To simplify testing, you may use [fear-rspec](https://github.com/bolshakov/fear-rspec) gem. It
@@ -882,6 +1234,7 @@ provides a bunch of rspec matchers.
882
1234
 
883
1235
  ## Alternatives
884
1236
 
1237
+ * [algebrick](https://github.com/pitr-ch/algebrick)
885
1238
  * [deterministic](https://github.com/pzol/deterministic)
886
1239
  * [dry-monads](https://github.com/dry-rb/dry-monads)
887
1240
  * [kleisli](https://github.com/txus/kleisli)