fear 1.0.0 → 1.1.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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +4 -60
  3. data/.travis.yml +8 -4
  4. data/CHANGELOG.md +7 -1
  5. data/Gemfile +5 -3
  6. data/Gemfile.lock +18 -20
  7. data/README.md +28 -14
  8. data/Rakefile +61 -60
  9. data/examples/pattern_extracting.rb +8 -6
  10. data/examples/pattern_matching_binary_tree_set.rb +4 -2
  11. data/examples/pattern_matching_number_in_words.rb +46 -42
  12. data/fear.gemspec +29 -27
  13. data/lib/fear.rb +44 -37
  14. data/lib/fear/await.rb +33 -0
  15. data/lib/fear/awaitable.rb +28 -0
  16. data/lib/fear/either.rb +2 -0
  17. data/lib/fear/either_api.rb +2 -0
  18. data/lib/fear/either_pattern_match.rb +2 -0
  19. data/lib/fear/empty_partial_function.rb +3 -1
  20. data/lib/fear/extractor.rb +30 -28
  21. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +2 -0
  22. data/lib/fear/extractor/any_matcher.rb +2 -0
  23. data/lib/fear/extractor/array_head_matcher.rb +2 -0
  24. data/lib/fear/extractor/array_matcher.rb +2 -0
  25. data/lib/fear/extractor/array_splat_matcher.rb +2 -0
  26. data/lib/fear/extractor/empty_list_matcher.rb +2 -0
  27. data/lib/fear/extractor/extractor_matcher.rb +5 -3
  28. data/lib/fear/extractor/grammar.rb +5 -3
  29. data/lib/fear/extractor/identifier_matcher.rb +2 -0
  30. data/lib/fear/extractor/matcher.rb +5 -3
  31. data/lib/fear/extractor/matcher/and.rb +3 -1
  32. data/lib/fear/extractor/named_array_splat_matcher.rb +2 -0
  33. data/lib/fear/extractor/pattern.rb +7 -5
  34. data/lib/fear/extractor/typed_identifier_matcher.rb +2 -0
  35. data/lib/fear/extractor/value_matcher.rb +2 -0
  36. data/lib/fear/extractor_api.rb +2 -0
  37. data/lib/fear/failure.rb +2 -0
  38. data/lib/fear/failure_pattern_match.rb +2 -0
  39. data/lib/fear/for.rb +4 -2
  40. data/lib/fear/for_api.rb +3 -1
  41. data/lib/fear/future.rb +141 -64
  42. data/lib/fear/future_api.rb +2 -0
  43. data/lib/fear/left.rb +3 -1
  44. data/lib/fear/left_pattern_match.rb +2 -0
  45. data/lib/fear/none.rb +4 -2
  46. data/lib/fear/none_pattern_match.rb +2 -0
  47. data/lib/fear/option.rb +3 -1
  48. data/lib/fear/option_api.rb +2 -0
  49. data/lib/fear/option_pattern_match.rb +2 -0
  50. data/lib/fear/partial_function.rb +10 -8
  51. data/lib/fear/partial_function/and_then.rb +4 -2
  52. data/lib/fear/partial_function/any.rb +2 -0
  53. data/lib/fear/partial_function/combined.rb +3 -1
  54. data/lib/fear/partial_function/empty.rb +2 -0
  55. data/lib/fear/partial_function/guard.rb +7 -5
  56. data/lib/fear/partial_function/guard/and.rb +2 -0
  57. data/lib/fear/partial_function/guard/and3.rb +2 -0
  58. data/lib/fear/partial_function/guard/or.rb +2 -0
  59. data/lib/fear/partial_function/lifted.rb +2 -0
  60. data/lib/fear/partial_function/or_else.rb +3 -1
  61. data/lib/fear/partial_function_class.rb +3 -1
  62. data/lib/fear/pattern_match.rb +3 -1
  63. data/lib/fear/pattern_matching_api.rb +3 -1
  64. data/lib/fear/promise.rb +11 -3
  65. data/lib/fear/right.rb +3 -1
  66. data/lib/fear/right_biased.rb +4 -2
  67. data/lib/fear/right_pattern_match.rb +2 -0
  68. data/lib/fear/some.rb +2 -0
  69. data/lib/fear/some_pattern_match.rb +2 -0
  70. data/lib/fear/struct.rb +235 -0
  71. data/lib/fear/success.rb +2 -0
  72. data/lib/fear/success_pattern_match.rb +2 -0
  73. data/lib/fear/try.rb +2 -0
  74. data/lib/fear/try_api.rb +2 -0
  75. data/lib/fear/try_pattern_match.rb +2 -0
  76. data/lib/fear/unit.rb +6 -2
  77. data/lib/fear/utils.rb +4 -2
  78. data/lib/fear/version.rb +4 -1
  79. data/spec/fear/done_spec.rb +7 -5
  80. data/spec/fear/either/mixin_spec.rb +4 -2
  81. data/spec/fear/either_pattern_match_spec.rb +10 -8
  82. data/spec/fear/extractor/array_matcher_spec.rb +65 -63
  83. data/spec/fear/extractor/extractor_matcher_spec.rb +64 -62
  84. data/spec/fear/extractor/grammar_array_spec.rb +5 -3
  85. data/spec/fear/extractor/identified_matcher_spec.rb +18 -16
  86. data/spec/fear/extractor/identifier_matcher_spec.rb +26 -24
  87. data/spec/fear/extractor/pattern_spec.rb +17 -15
  88. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +23 -21
  89. data/spec/fear/extractor/value_matcher_number_spec.rb +29 -27
  90. data/spec/fear/extractor/value_matcher_string_spec.rb +27 -25
  91. data/spec/fear/extractor/value_matcher_symbol_spec.rb +24 -22
  92. data/spec/fear/extractor_api_spec.rb +70 -68
  93. data/spec/fear/extractor_spec.rb +23 -21
  94. data/spec/fear/failure_spec.rb +59 -57
  95. data/spec/fear/for_spec.rb +19 -17
  96. data/spec/fear/future_spec.rb +456 -240
  97. data/spec/fear/guard_spec.rb +26 -24
  98. data/spec/fear/left_spec.rb +60 -58
  99. data/spec/fear/none_spec.rb +36 -34
  100. data/spec/fear/option/mixin_spec.rb +9 -7
  101. data/spec/fear/option_pattern_match_spec.rb +10 -8
  102. data/spec/fear/partial_function/empty_spec.rb +12 -10
  103. data/spec/fear/partial_function_and_then_spec.rb +39 -37
  104. data/spec/fear/partial_function_composition_spec.rb +46 -44
  105. data/spec/fear/partial_function_or_else_spec.rb +92 -90
  106. data/spec/fear/partial_function_spec.rb +46 -44
  107. data/spec/fear/pattern_match_spec.rb +31 -29
  108. data/spec/fear/promise_spec.rb +19 -17
  109. data/spec/fear/right_biased/left.rb +28 -26
  110. data/spec/fear/right_biased/right.rb +51 -49
  111. data/spec/fear/right_spec.rb +58 -56
  112. data/spec/fear/some_spec.rb +30 -28
  113. data/spec/fear/success_spec.rb +50 -48
  114. data/spec/fear/try/mixin_spec.rb +5 -3
  115. data/spec/fear/try_pattern_match_spec.rb +10 -8
  116. data/spec/fear/utils_spec.rb +16 -14
  117. data/spec/spec_helper.rb +7 -5
  118. data/spec/struct_spec.rb +226 -0
  119. metadata +18 -13
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # Match against array splat, E.g. `[1, 2, *]` or `[1, 2, *_]`
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # Always match, E.g. `_ : Integer` without capturing variable
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # Part of recursive array matcher. Match against its head.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # Recursive array matcher. Match against its head and tail
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # @abstract
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # Match only if array is empty
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # Match and extract pattern using registered extractor objects
@@ -19,19 +21,19 @@ module Fear
19
21
 
20
22
  def defined_at?(other)
21
23
  extractor
22
- .call(other)
24
+ .(other)
23
25
  .map { |v| arguments_matcher.defined_at?(v) }
24
26
  .get_or_else(false)
25
27
  end
26
28
 
27
29
  def call_or_else(arg)
28
- extractor.call(arg)
30
+ extractor.(arg)
29
31
  .map { |v| arguments_matcher.call_or_else(v) { yield arg } }
30
32
  .get_or_else { yield arg }
31
33
  end
32
34
 
33
35
  def failure_reason(other)
34
- extractor.call(other).match do |m|
36
+ extractor.(other).match do |m|
35
37
  m.some(->(v) { arguments_matcher.defined_at?(v) }) { Fear.none }
36
38
  m.some { |v| arguments_matcher.failure_reason(v) }
37
39
  m.none { super }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # This module contains AST nodes for GrammarParser
@@ -56,7 +58,7 @@ module Fear
56
58
  end
57
59
 
58
60
  def value
59
- text_value.to_f
61
+ Float(text_value)
60
62
  end
61
63
  end
62
64
 
@@ -66,7 +68,7 @@ module Fear
66
68
  end
67
69
 
68
70
  def value
69
- text_value.to_i
71
+ Integer(text_value, 10)
70
72
  end
71
73
  end
72
74
 
@@ -80,7 +82,7 @@ module Fear
80
82
  end
81
83
  end
82
84
 
83
- require 'yaml'
85
+ require "yaml"
84
86
 
85
87
  class DoubleQuotedStringLiteral < StringLiteral
86
88
  def to_matcher
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  class IdentifierMatcher < Matcher
@@ -1,10 +1,12 @@
1
- require 'ostruct'
1
+ # frozen_string_literal: true
2
+
3
+ require "ostruct"
2
4
 
3
5
  module Fear
4
6
  module Extractor
5
7
  # @abstract abstract matcher to inherit from.
6
8
  class Matcher < OpenStruct
7
- autoload :And, 'fear/extractor/matcher/and'
9
+ autoload :And, "fear/extractor/matcher/and"
8
10
 
9
11
  EMPTY_HASH = {}.freeze
10
12
  EMPTY_ARRAY = [].freeze
@@ -46,7 +48,7 @@ module Fear
46
48
  if defined_at?(other)
47
49
  Fear.none
48
50
  else
49
- Fear.some("Expected `#{other.inspect}` to match:\n#{input}\n#{'~' * input_position}^")
51
+ Fear.some("Expected `#{other.inspect}` to match:\n#{input}\n#{"~" * input_position}^")
50
52
  end
51
53
  end
52
54
  end
@@ -1,4 +1,6 @@
1
- require 'ostruct'
1
+ # frozen_string_literal: true
2
+
3
+ require "ostruct"
2
4
 
3
5
  module Fear
4
6
  module Extractor
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # Match against array splat, and capture rest of an array
@@ -1,11 +1,13 @@
1
- require 'lru_redux'
1
+ # frozen_string_literal: true
2
+
3
+ require "lru_redux"
2
4
 
3
5
  module Fear
4
6
  module Extractor
5
7
  # Parse pattern. Used within +Fear[]+
6
8
  class Pattern
7
9
  DEFAULT_PATTERN_CACHE_SIZE = 10_000
8
- @pattern_cache = LruRedux::Cache.new(ENV.fetch('FEAR_PATTERNS_CACHE_SIZE', DEFAULT_PATTERN_CACHE_SIZE))
10
+ @pattern_cache = LruRedux::Cache.new(ENV.fetch("FEAR_PATTERNS_CACHE_SIZE", DEFAULT_PATTERN_CACHE_SIZE))
9
11
 
10
12
  class << self
11
13
  attr_reader :pattern_cache
@@ -41,14 +43,14 @@ module Fear
41
43
  end
42
44
 
43
45
  def failure_reason(other)
44
- matcher.failure_reason(other).get_or_else { 'It matches' }
46
+ matcher.failure_reason(other).get_or_else { "It matches" }
45
47
  end
46
48
 
47
49
  private def syntax_error_message(parser, pattern)
48
50
  parser.failure_reason =~ /^(Expected .+) after/m
49
- "#{Regexp.last_match(1).gsub("\n", '$NEWLINE')}:\n" +
51
+ "#{Regexp.last_match(1).gsub("\n", "$NEWLINE")}:\n" +
50
52
  pattern.split("\n")[parser.failure_line - 1] + "\n" \
51
- "#{'~' * (parser.failure_column - 1)}^\n"
53
+ "#{"~" * (parser.failure_column - 1)}^\n"
52
54
  end
53
55
  end
54
56
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # Match and capture identifier with specific type. E.g. +foo : Integer+
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module Extractor
3
5
  # Match against values -- true, false, 1, "foo" etc.
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module ExtractorApi
3
5
  # Allows to pattern match and extract matcher variables
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  class Failure
3
5
  include Try
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # @api private
3
5
  class FailurePatternMatch < Fear::TryPatternMatch
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # @api private
3
5
  # @see Fear.for
4
6
  module For
5
- module_function # rubocop: disable Style/AccessModifierDeclarations
7
+ module_function
6
8
 
7
9
  # @param monads [<Fear::Option, Fear::Either, Fear::Try, Proc>]
8
10
  #
@@ -30,7 +32,7 @@ module Fear
30
32
 
31
33
  private def resolve(monad_or_proc, inner_values)
32
34
  if monad_or_proc.respond_to?(:call)
33
- monad_or_proc.call(*inner_values)
35
+ monad_or_proc.(*inner_values)
34
36
  else
35
37
  monad_or_proc
36
38
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  module ForApi
3
5
  # Syntactic sugar for composition of multiple monadic operations. It supports two such
@@ -60,7 +62,7 @@ module Fear
60
62
  # @return [{#map, #flat_map}]
61
63
  #
62
64
  def for(*monads, &block)
63
- Fear::For.call(monads, &block)
65
+ Fear::For.(monads, &block)
64
66
  end
65
67
  end
66
68
  end
@@ -1,20 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
- require 'concurrent'
4
+ require "concurrent"
3
5
  rescue LoadError
4
6
  puts "You must add 'concurrent-ruby' to your Gemfile in order to use Fear::Future"
5
7
  end
6
- require_relative 'promise'
7
8
 
8
9
  module Fear
9
10
  # Asynchronous computations that yield futures are created
10
11
  # with the +Fear.future+ call:
11
12
  #
12
- # @example
13
- # success = "Hello"
14
- # f = Fear.future { success + ' future!' }
15
- # f.on_success do |result|
16
- # puts result
17
- # end
13
+ # success = "Hello"
14
+ # f = Fear.future { success + ' future!' }
15
+ # f.on_success do |result|
16
+ # puts result
17
+ # end
18
18
  #
19
19
  # Multiple callbacks may be registered; there is no guarantee
20
20
  # that they will be executed in a particular order.
@@ -23,30 +23,45 @@ module Fear
23
23
  # that the future failed. Futures obtained through combinators
24
24
  # have the same error as the future they were obtained from.
25
25
  #
26
- # @example
27
- # f = Fear.future { 5 }
28
- # g = Fear.future { 3 }
29
- # f.flat_map do |x|
30
- # g.map { |y| x + y }
31
- # end
26
+ # f = Fear.future { 5 }
27
+ # g = Fear.future { 3 }
28
+ # f.flat_map do |x|
29
+ # g.map { |y| x + y }
30
+ # end
31
+ #
32
+ # The same program may be written using +Fear.for+
33
+ #
34
+ # Fear.for(Fear.future { 5 }, Fear.future { 3 }) do |x, y|
35
+ # x + y
36
+ # end
37
+ #
38
+ # Futures use +Concurrent::Promise+ under the hood. +Fear.future+ accepts optional configuration Hash passed
39
+ # directly to underlying promise. For example, run it on custom thread pool.
32
40
  #
33
- # @example the same program may be written using +Fear.for+
34
- # Fear.for(Fear.future { 5 }, Fear.future { 3 }) do |x, y|
35
- # x + y
36
- # end
41
+ # require 'open-uri'
42
+ #
43
+ # future = Fear.future(executor: :io) { open('https://example.com/') }
44
+ #
45
+ # future.map(executor: :fast, &:read).each do |body|
46
+ # puts "#{body}"
47
+ # end
37
48
  #
38
49
  # @see https://github.com/scala/scala/blob/2.11.x/src/library/scala/concurrent/Future.scala
39
50
  #
40
51
  class Future
52
+ include Awaitable
53
+
41
54
  # @param promise [nil, Concurrent::Future] converts
42
- # +Concurrent::Future+ into +Fear::Future+.
55
+ # +Concurrent::Promise+ into +Fear::Future+.
43
56
  # @param options [see Concurrent::Future] options will be passed
44
- # directly to +Concurrent::Future+
57
+ # directly to +Concurrent::Promise+
45
58
  # @yield given block and evaluate it in the future.
46
59
  # @api private
60
+ # @see Fear.future
61
+ #
47
62
  def initialize(promise = nil, **options, &block)
48
63
  if block_given? && promise
49
- raise ArgumentError, 'pass block or future'
64
+ raise ArgumentError, "pass block or future"
50
65
  end
51
66
 
52
67
  @options = options
@@ -57,11 +72,11 @@ module Fear
57
72
  attr_reader :promise
58
73
  private :promise
59
74
 
60
- # Calls the provided callback When this future is completed successfully.
75
+ # Calls the provided callback when this future is completed successfully.
61
76
  #
62
77
  # If the future has already been completed with a value,
63
78
  # this will either be applied immediately or be scheduled asynchronously.
64
- # @yieldparam [value]
79
+ # @yieldparam [any] value
65
80
  # @return [self]
66
81
  # @see #transform
67
82
  #
@@ -76,6 +91,24 @@ module Fear
76
91
  end
77
92
  end
78
93
 
94
+ # When this future is completed successfully match against its result
95
+ #
96
+ # If the future has already been completed with a value,
97
+ # this will either be applied immediately or be scheduled asynchronously.
98
+ # @yieldparam [Fear::PatternMatch] m
99
+ # @return [self]
100
+ #
101
+ # @example
102
+ # Fear.future { }.on_success_match do |m|
103
+ # m.case(42) { ... }
104
+ # end
105
+ #
106
+ def on_success_match
107
+ on_success do |value|
108
+ Fear.matcher { |m| yield(m) }.call_or_else(value, &:itself)
109
+ end
110
+ end
111
+
79
112
  # When this future is completed with a failure apply the provided callback to the error.
80
113
  #
81
114
  # If the future has already been completed with a failure,
@@ -100,6 +133,26 @@ module Fear
100
133
  end
101
134
  end
102
135
 
136
+ # When this future is completed with a failure match against the error.
137
+ #
138
+ # If the future has already been completed with a failure,
139
+ # this will either be applied immediately or be scheduled asynchronously.
140
+ #
141
+ # Will not be called in case that the future is completed with a value.
142
+ # @yieldparam [Fear::PatternMatch] m
143
+ # @return [self]
144
+ #
145
+ # @example
146
+ # Fear.future { }.on_failure_match do |m|
147
+ # m.case(HTTPError) { |error| ... }
148
+ # end
149
+ #
150
+ def on_failure_match
151
+ on_failure do |error|
152
+ Fear.matcher { |m| yield(m) }.call_or_else(error, &:itself)
153
+ end
154
+ end
155
+
103
156
  # When this future is completed call the provided block.
104
157
  #
105
158
  # If the future has already been completed,
@@ -119,6 +172,26 @@ module Fear
119
172
  self
120
173
  end
121
174
 
175
+ # When this future is completed match against result.
176
+ #
177
+ # If the future has already been completed,
178
+ # this will either be applied immediately or be scheduled asynchronously.
179
+ # @yieldparam [Fear::TryPatternMatch]
180
+ # @return [self]
181
+ #
182
+ # @example
183
+ # Fear.future { }.on_complete_match do |m|
184
+ # m.success { |result| }
185
+ # m.failure { |error| }
186
+ # end
187
+ #
188
+ def on_complete_match
189
+ promise.add_observer do |_time, try, _error|
190
+ Fear::Try.matcher { |m| yield(m) }.call_or_else(try, &:itself)
191
+ end
192
+ self
193
+ end
194
+
122
195
  # Returns whether the future has already been completed with
123
196
  # a value or an error.
124
197
  #
@@ -177,11 +250,9 @@ module Fear
177
250
  #
178
251
  def transform(success, failure)
179
252
  promise = Promise.new(@options)
180
- on_complete do |try|
181
- try.match do |m|
182
- m.success { |value| promise.success(success.call(value)) }
183
- m.failure { |error| promise.failure(failure.call(error)) }
184
- end
253
+ on_complete_match do |m|
254
+ m.success { |value| promise.success(success.(value)) }
255
+ m.failure { |error| promise.failure(failure.(error)) }
185
256
  end
186
257
  promise.to_future
187
258
  end
@@ -222,17 +293,15 @@ module Fear
222
293
  # end
223
294
  # end
224
295
  #
225
- def flat_map # rubocop: disable Metrics/MethodLength
296
+ def flat_map
226
297
  promise = Promise.new(@options)
227
- on_complete do |result|
228
- result.match do |m|
229
- m.failure { promise.complete!(result) }
230
- m.success do |value|
231
- begin
232
- yield(value).on_complete { |callback_result| promise.complete!(callback_result) }
233
- rescue StandardError => error
234
- promise.failure!(error)
235
- end
298
+ on_complete_match do |m|
299
+ m.case(Fear::Failure) { |failure| promise.complete!(failure) }
300
+ m.success do |value|
301
+ begin
302
+ yield(value).on_complete { |callback_result| promise.complete!(callback_result) }
303
+ rescue StandardError => error
304
+ promise.failure!(error)
236
305
  end
237
306
  end
238
307
  end
@@ -261,7 +330,7 @@ module Fear
261
330
  if yield(result)
262
331
  result
263
332
  else
264
- raise NoSuchElementError, '#select predicate is not satisfied'
333
+ raise NoSuchElementError, "#select predicate is not satisfied"
265
334
  end
266
335
  end
267
336
  end
@@ -306,19 +375,17 @@ module Fear
306
375
  # future1.zip(future2) #=> returns the same result as Fear.future { [call_service1, call_service2] },
307
376
  # # but it performs two calls asynchronously
308
377
  #
309
- def zip(other) # rubocop: disable Metrics/MethodLength
378
+ def zip(other)
310
379
  promise = Promise.new(@options)
311
- on_complete do |try_of_self|
312
- try_of_self.match do |m|
313
- m.success do |value|
314
- other.on_complete do |try_of_other|
315
- promise.complete!(try_of_other.map { |other_value| [value, other_value] })
316
- end
317
- end
318
- m.failure do |error|
319
- promise.failure!(error)
380
+ on_complete_match do |m|
381
+ m.success do |value|
382
+ other.on_complete do |other_try|
383
+ promise.complete!(other_try.map { |other_value| [value, other_value] })
320
384
  end
321
385
  end
386
+ m.failure do |error|
387
+ promise.failure!(error)
388
+ end
322
389
  end
323
390
 
324
391
  promise.to_future
@@ -338,26 +405,20 @@ module Fear
338
405
  # g = Fear.future { 5 }
339
406
  # f.fallback_to(g) # evaluates to 5
340
407
  #
341
- # rubocop: disable Metrics/MethodLength
342
408
  def fallback_to(fallback)
343
409
  promise = Promise.new(@options)
344
- on_complete do |try|
345
- try.match do |m|
346
- m.success { |value| promise.complete!(value) }
347
- m.failure do |error|
348
- fallback.on_complete do |fallback_try|
349
- fallback_try.match do |m2|
350
- m2.success { |value| promise.complete!(value) }
351
- m2.failure { promise.failure!(error) }
352
- end
353
- end
410
+ on_complete_match do |m|
411
+ m.success { |value| promise.complete!(value) }
412
+ m.failure do |error|
413
+ fallback.on_complete_match do |m2|
414
+ m2.success { |value| promise.complete!(value) }
415
+ m2.failure { promise.failure!(error) }
354
416
  end
355
417
  end
356
418
  end
357
419
 
358
420
  promise.to_future
359
421
  end
360
- # rubocop: enable Metrics/MethodLength
361
422
 
362
423
  # Applies the side-effecting block to the result of +self+ future,
363
424
  # and returns a new future with the result of this future.
@@ -373,22 +434,38 @@ module Fear
373
434
  # @example The following example prints out +5+:
374
435
  # f = Fear.future { 5 }
375
436
  # f.and_then do
376
- # fail 'runtime error'
437
+ # m.success { }fail| 'runtime error' }
377
438
  # end.and_then do |m|
378
439
  # m.success { |value| puts value } # it evaluates this branch
379
440
  # m.failure { |error| puts error.massage }
380
441
  # end
381
442
  #
382
- def and_then(&block)
443
+ def and_then
383
444
  promise = Promise.new(@options)
384
445
  on_complete do |try|
385
- Fear.try { try.match(&block) }
446
+ Fear.try do
447
+ Fear::Try.matcher { |m| yield(m) }.call_or_else(try, &:itself)
448
+ end
386
449
  promise.complete!(try)
387
450
  end
388
451
 
389
452
  promise.to_future
390
453
  end
391
454
 
455
+ # @api private
456
+ def __result__(at_most)
457
+ __ready__(at_most).value.get_or_else { raise "promise not completed" }
458
+ end
459
+
460
+ # @api private
461
+ def __ready__(at_most)
462
+ if promise.wait(at_most).complete?
463
+ self
464
+ else
465
+ raise Timeout::Error
466
+ end
467
+ end
468
+
392
469
  class << self
393
470
  # Creates an already completed +Future+ with the specified error.
394
471
  # @param exception [StandardError]