fear 0.11.0 → 1.0.0

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