fear 0.9.0 → 1.2.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 (155) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/rubocop.yml +39 -0
  3. data/.github/workflows/spec.yml +42 -0
  4. data/.gitignore +0 -1
  5. data/.rubocop.yml +4 -12
  6. data/.simplecov +17 -0
  7. data/CHANGELOG.md +40 -0
  8. data/Gemfile +5 -2
  9. data/Gemfile.lock +130 -0
  10. data/LICENSE.txt +1 -1
  11. data/README.md +1293 -97
  12. data/Rakefile +369 -1
  13. data/benchmarks/README.md +1 -0
  14. data/benchmarks/dry_do_vs_fear_for.txt +11 -0
  15. data/benchmarks/dry_some_fmap_vs_fear_some_map.txt +11 -0
  16. data/benchmarks/factorial.txt +16 -0
  17. data/benchmarks/fear_gaurd_and1_vs_new.txt +13 -0
  18. data/benchmarks/fear_gaurd_and2_vs_and.txt +13 -0
  19. data/benchmarks/fear_gaurd_and3_vs_and_and.txt +13 -0
  20. data/benchmarks/fear_pattern_extracting_with_vs_without_cache.txt +11 -0
  21. data/benchmarks/fear_pattern_matching_construction_vs_execution.txt +13 -0
  22. data/benchmarks/pattern_matching_dry_vs_qo_vs_fear_try.txt +14 -0
  23. data/benchmarks/pattern_matching_qo_vs_fear_pattern_extraction.txt +11 -0
  24. data/benchmarks/pattern_matching_qo_vs_fear_try_execution.txt +11 -0
  25. data/examples/pattern_extracting.rb +17 -0
  26. data/examples/pattern_extracting_ruby2.7.rb +15 -0
  27. data/examples/pattern_matching_binary_tree_set.rb +101 -0
  28. data/examples/pattern_matching_number_in_words.rb +60 -0
  29. data/fear.gemspec +34 -23
  30. data/lib/dry/types/fear.rb +8 -0
  31. data/lib/dry/types/fear/option.rb +125 -0
  32. data/lib/fear.rb +65 -15
  33. data/lib/fear/await.rb +33 -0
  34. data/lib/fear/awaitable.rb +28 -0
  35. data/lib/fear/either.rb +131 -71
  36. data/lib/fear/either_api.rb +23 -0
  37. data/lib/fear/either_pattern_match.rb +53 -0
  38. data/lib/fear/empty_partial_function.rb +38 -0
  39. data/lib/fear/extractor.rb +112 -0
  40. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +10 -0
  41. data/lib/fear/extractor/any_matcher.rb +17 -0
  42. data/lib/fear/extractor/array_head_matcher.rb +36 -0
  43. data/lib/fear/extractor/array_matcher.rb +40 -0
  44. data/lib/fear/extractor/array_splat_matcher.rb +16 -0
  45. data/lib/fear/extractor/empty_list_matcher.rb +20 -0
  46. data/lib/fear/extractor/extractor_matcher.rb +44 -0
  47. data/lib/fear/extractor/grammar.rb +203 -0
  48. data/lib/fear/extractor/grammar.treetop +129 -0
  49. data/lib/fear/extractor/identifier_matcher.rb +18 -0
  50. data/lib/fear/extractor/matcher.rb +53 -0
  51. data/lib/fear/extractor/matcher/and.rb +38 -0
  52. data/lib/fear/extractor/named_array_splat_matcher.rb +17 -0
  53. data/lib/fear/extractor/pattern.rb +58 -0
  54. data/lib/fear/extractor/typed_identifier_matcher.rb +26 -0
  55. data/lib/fear/extractor/value_matcher.rb +19 -0
  56. data/lib/fear/extractor_api.rb +35 -0
  57. data/lib/fear/failure.rb +46 -14
  58. data/lib/fear/failure_pattern_match.rb +10 -0
  59. data/lib/fear/for.rb +37 -95
  60. data/lib/fear/for_api.rb +68 -0
  61. data/lib/fear/future.rb +497 -0
  62. data/lib/fear/future_api.rb +21 -0
  63. data/lib/fear/left.rb +19 -2
  64. data/lib/fear/left_pattern_match.rb +11 -0
  65. data/lib/fear/none.rb +67 -3
  66. data/lib/fear/none_pattern_match.rb +14 -0
  67. data/lib/fear/option.rb +120 -56
  68. data/lib/fear/option_api.rb +40 -0
  69. data/lib/fear/option_pattern_match.rb +48 -0
  70. data/lib/fear/partial_function.rb +176 -0
  71. data/lib/fear/partial_function/and_then.rb +50 -0
  72. data/lib/fear/partial_function/any.rb +28 -0
  73. data/lib/fear/partial_function/combined.rb +53 -0
  74. data/lib/fear/partial_function/empty.rb +10 -0
  75. data/lib/fear/partial_function/guard.rb +80 -0
  76. data/lib/fear/partial_function/guard/and.rb +38 -0
  77. data/lib/fear/partial_function/guard/and3.rb +41 -0
  78. data/lib/fear/partial_function/guard/or.rb +38 -0
  79. data/lib/fear/partial_function/lifted.rb +23 -0
  80. data/lib/fear/partial_function/or_else.rb +64 -0
  81. data/lib/fear/partial_function_class.rb +38 -0
  82. data/lib/fear/pattern_match.rb +114 -0
  83. data/lib/fear/pattern_matching_api.rb +137 -0
  84. data/lib/fear/promise.rb +95 -0
  85. data/lib/fear/right.rb +20 -2
  86. data/lib/fear/right_biased.rb +6 -14
  87. data/lib/fear/right_pattern_match.rb +11 -0
  88. data/lib/fear/some.rb +55 -3
  89. data/lib/fear/some_pattern_match.rb +13 -0
  90. data/lib/fear/struct.rb +248 -0
  91. data/lib/fear/success.rb +35 -5
  92. data/lib/fear/success_pattern_match.rb +12 -0
  93. data/lib/fear/try.rb +136 -79
  94. data/lib/fear/try_api.rb +33 -0
  95. data/lib/fear/try_pattern_match.rb +33 -0
  96. data/lib/fear/unit.rb +32 -0
  97. data/lib/fear/utils.rb +39 -14
  98. data/lib/fear/version.rb +4 -1
  99. data/spec/dry/types/fear/option/constrained_spec.rb +22 -0
  100. data/spec/dry/types/fear/option/core_spec.rb +77 -0
  101. data/spec/dry/types/fear/option/default_spec.rb +21 -0
  102. data/spec/dry/types/fear/option/hash_spec.rb +58 -0
  103. data/spec/dry/types/fear/option/option_spec.rb +97 -0
  104. data/spec/fear/awaitable_spec.rb +17 -0
  105. data/spec/fear/done_spec.rb +8 -6
  106. data/spec/fear/either/mixin_spec.rb +17 -0
  107. data/spec/fear/either_pattern_match_spec.rb +37 -0
  108. data/spec/fear/either_pattern_matching_spec.rb +28 -0
  109. data/spec/fear/extractor/array_matcher_spec.rb +230 -0
  110. data/spec/fear/extractor/extractor_matcher_spec.rb +153 -0
  111. data/spec/fear/extractor/grammar_array_spec.rb +25 -0
  112. data/spec/fear/extractor/identified_matcher_spec.rb +49 -0
  113. data/spec/fear/extractor/identifier_matcher_spec.rb +68 -0
  114. data/spec/fear/extractor/pattern_spec.rb +34 -0
  115. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +64 -0
  116. data/spec/fear/extractor/value_matcher_number_spec.rb +79 -0
  117. data/spec/fear/extractor/value_matcher_string_spec.rb +88 -0
  118. data/spec/fear/extractor/value_matcher_symbol_spec.rb +71 -0
  119. data/spec/fear/extractor_api_spec.rb +115 -0
  120. data/spec/fear/extractor_spec.rb +61 -0
  121. data/spec/fear/failure_spec.rb +145 -45
  122. data/spec/fear/for_spec.rb +57 -67
  123. data/spec/fear/future_spec.rb +691 -0
  124. data/spec/fear/guard_spec.rb +103 -0
  125. data/spec/fear/left_spec.rb +112 -46
  126. data/spec/fear/none_spec.rb +114 -16
  127. data/spec/fear/option/mixin_spec.rb +39 -0
  128. data/spec/fear/option_pattern_match_spec.rb +35 -0
  129. data/spec/fear/option_pattern_matching_spec.rb +34 -0
  130. data/spec/fear/option_spec.rb +121 -8
  131. data/spec/fear/partial_function/empty_spec.rb +38 -0
  132. data/spec/fear/partial_function_and_then_spec.rb +147 -0
  133. data/spec/fear/partial_function_composition_spec.rb +82 -0
  134. data/spec/fear/partial_function_or_else_spec.rb +276 -0
  135. data/spec/fear/partial_function_spec.rb +239 -0
  136. data/spec/fear/pattern_match_spec.rb +93 -0
  137. data/spec/fear/pattern_matching_api_spec.rb +31 -0
  138. data/spec/fear/promise_spec.rb +96 -0
  139. data/spec/fear/right_biased/left.rb +29 -32
  140. data/spec/fear/right_biased/right.rb +51 -54
  141. data/spec/fear/right_spec.rb +109 -41
  142. data/spec/fear/some_spec.rb +80 -15
  143. data/spec/fear/success_spec.rb +99 -32
  144. data/spec/fear/try/mixin_spec.rb +19 -0
  145. data/spec/fear/try_pattern_match_spec.rb +37 -0
  146. data/spec/fear/try_pattern_matching_spec.rb +34 -0
  147. data/spec/fear/utils_spec.rb +16 -14
  148. data/spec/spec_helper.rb +13 -7
  149. data/spec/struct_pattern_matching_spec.rb +36 -0
  150. data/spec/struct_spec.rb +226 -0
  151. data/spec/support/dry_types.rb +6 -0
  152. metadata +320 -29
  153. data/.travis.yml +0 -9
  154. data/lib/fear/done.rb +0 -22
  155. data/lib/fear/for/evaluation_context.rb +0 -91
@@ -1,27 +1,77 @@
1
- require 'dry-equalizer'
2
- require 'fear/version'
1
+ # frozen_string_literal: true
2
+
3
+ require "fear/either_api"
4
+ require "fear/extractor_api"
5
+ require "fear/for_api"
6
+ require "fear/future_api"
7
+ require "fear/option_api"
8
+ require "fear/pattern_matching_api"
9
+ require "fear/try_api"
10
+ require "fear/version"
3
11
 
4
12
  module Fear
5
13
  Error = Class.new(StandardError)
14
+ public_constant :Error
15
+
6
16
  IllegalStateException = Class.new(Error)
17
+ public_constant :IllegalStateException
18
+
19
+ MatchError = Class.new(Error)
20
+ public_constant :MatchError
21
+
7
22
  NoSuchElementError = Class.new(Error)
23
+ public_constant :NoSuchElementError
24
+
25
+ PatternSyntaxError = Class.new(Error)
26
+ public_constant :PatternSyntaxError
27
+
28
+ extend EitherApi
29
+ extend ExtractorApi
30
+ extend ForApi
31
+ extend FutureApi
32
+ extend OptionApi
33
+ extend PatternMatchingApi
34
+ extend TryApi
35
+
36
+ autoload :EmptyPartialFunction, "fear/empty_partial_function"
37
+ autoload :PartialFunction, "fear/partial_function"
38
+ autoload :PartialFunctionClass, "fear/partial_function_class"
39
+ autoload :PatternMatch, "fear/pattern_match"
40
+ autoload :Extractor, "fear/extractor"
41
+
42
+ autoload :Unit, "fear/unit"
43
+ autoload :For, "fear/for"
44
+ autoload :RightBiased, "fear/right_biased"
45
+ autoload :Utils, "fear/utils"
46
+
47
+ autoload :None, "fear/none"
48
+ autoload :NoneClass, "fear/none"
49
+ autoload :NonePatternMatch, "fear/none_pattern_match"
50
+ autoload :Option, "fear/option"
51
+ autoload :OptionPatternMatch, "fear/option_pattern_match"
52
+ autoload :Some, "fear/some"
53
+ autoload :SomePatternMatch, "fear/some_pattern_match"
8
54
 
9
- autoload :Done, 'fear/done'
10
- autoload :For, 'fear/for'
11
- autoload :RightBiased, 'fear/right_biased'
12
- autoload :Utils, 'fear/utils'
55
+ autoload :Failure, "fear/failure"
56
+ autoload :FailurePatternMatch, "fear/failure_pattern_match"
57
+ autoload :Success, "fear/success"
58
+ autoload :SuccessPatternMatch, "fear/success_pattern_match"
59
+ autoload :Try, "fear/try"
60
+ autoload :TryPatternMatch, "fear/try_pattern_match"
13
61
 
14
- autoload :Option, 'fear/option'
15
- autoload :Some, 'fear/some'
16
- autoload :None, 'fear/none'
62
+ autoload :Either, "fear/either"
63
+ autoload :EitherPatternMatch, "fear/either_pattern_match"
64
+ autoload :Left, "fear/left"
65
+ autoload :LeftPatternMatch, "fear/left_pattern_match"
66
+ autoload :Right, "fear/right"
67
+ autoload :RightPatternMatch, "fear/right_pattern_match"
17
68
 
18
- autoload :Try, 'fear/try'
19
- autoload :Success, 'fear/success'
20
- autoload :Failure, 'fear/failure'
69
+ autoload :Await, "fear/await"
70
+ autoload :Awaitable, "fear/awaitable"
71
+ autoload :Future, "fear/future"
72
+ autoload :Promise, "fear/promise"
21
73
 
22
- autoload :Either, 'fear/either'
23
- autoload :Left, 'fear/left'
24
- autoload :Right, 'fear/right'
74
+ autoload :Struct, "fear/struct"
25
75
 
26
76
  module Mixin
27
77
  include Either::Mixin
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ # You're strongly discouraged to use this module since it may lead to deadlocks,
5
+ # and reduced performance. Although, blocking may be useful in some cases (e.g. in tests)
6
+ #
7
+ # @see https://stackoverflow.com/questions/38155159/why-doesnt-scalas-future-have-a-get-getmaxduration-method-forcing-us-to
8
+ module Await
9
+ # Blocks until +Fear::Awaitable+ reached completed state and returns itself
10
+ # or raises +TimeoutError+
11
+ #
12
+ # @param awaitable [Fear::Awaitable]
13
+ # @param at_most [Fixnum] timeout in seconds
14
+ # @return [Fear::Awaitable]
15
+ # @raise [Timeout::Error]
16
+ #
17
+ module_function def ready(awaitable, at_most)
18
+ awaitable.__ready__(at_most)
19
+ end
20
+
21
+ # Blocks until +Fear::Awaitable+ reached completed state and returns its value
22
+ # or raises +TimeoutError+
23
+ #
24
+ # @param awaitable [Fear::Awaitable]
25
+ # @param at_most [Fixnum] timeout in seconds
26
+ # @return [any]
27
+ # @raise [Timeout::Error]
28
+ #
29
+ module_function def result(awaitable, at_most)
30
+ awaitable.__result__(at_most)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ # An object which may eventually be completed and awaited using blocking methods.
5
+ #
6
+ # @abstract
7
+ # @api private
8
+ # @see Fear::Await
9
+ module Awaitable
10
+ # Await +completed+ state of this +Awaitable+
11
+ #
12
+ # @param _at_most [Fixnum] maximum timeout in seconds
13
+ # @return [Fear::Awaitable]
14
+ # @raise [Timeout::Error]
15
+ def __ready__(_at_most)
16
+ raise NotImplementedError
17
+ end
18
+
19
+ # Await and return the result of this +Awaitable+
20
+ #
21
+ # @param _at_most [Fixnum] maximum timeout in seconds
22
+ # @return [any]
23
+ # @raise [Timeout::Error]
24
+ def __result__(_at_most)
25
+ raise NotImplementedError
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # Represents a value of one of two possible types (a disjoint union.)
3
5
  # An instance of +Either+ is either an instance of +Left+ or +Right+.
@@ -14,17 +16,20 @@ module Fear
14
16
  # @example
15
17
  # in = Readline.readline('Type Either a string or an Int: ', true)
16
18
  # result = begin
17
- # Right(Integer(in))
19
+ # Fear.right(Integer(in))
18
20
  # rescue ArgumentError
19
- # Left(in)
21
+ # Fear.left(in)
20
22
  # end
21
23
  #
22
- # puts(
23
- # result.reduce(
24
- # -> (x) { "You passed me the String: #{x}" },
25
- # -> (x) { "You passed me the Int: #{x}, which I will increment. #{x} + 1 = #{x+1}" }
26
- # )
27
- # )
24
+ # result.match do |m|
25
+ # m.right do |x|
26
+ # "You passed me the Int: #{x}, which I will increment. #{x} + 1 = #{x+1}"
27
+ # end
28
+ #
29
+ # m.left do |x|
30
+ # "You passed me the String: #{x}"
31
+ # end
32
+ # end
28
33
  #
29
34
  # Either is right-biased, which means that +Right+ is assumed to be the default case to
30
35
  # operate on. If it is +Left+, operations like +#map+, +#flat_map+, ... return the +Left+ value
@@ -37,21 +42,21 @@ module Fear
37
42
  # @yieldreturn [any]
38
43
  # @return [any]
39
44
  # @example
40
- # Right(42).get_or_else { 24/2 } #=> 42
41
- # Left('undefined').get_or_else { 24/2 } #=> 12
45
+ # Fear.right(42).get_or_else { 24/2 } #=> 42
46
+ # Fear.left('undefined').get_or_else { 24/2 } #=> 12
42
47
  # @overload get_or_else(default)
43
48
  # @return [any]
44
49
  # @example
45
- # Right(42).get_or_else(12) #=> 42
46
- # Left('undefined').get_or_else(12) #=> 12
50
+ # Fear.right(42).get_or_else(12) #=> 42
51
+ # Fear.left('undefined').get_or_else(12) #=> 12
47
52
  #
48
53
  # @!method or_else(&alternative)
49
54
  # Returns this +Right+ or the given alternative if this is a +Left+.
50
55
  # @return [Either]
51
56
  # @example
52
- # Right(42).or_else { Right(21) } #=> Right(42)
53
- # Left('unknown').or_else { Right(21) } #=> Right(21)
54
- # Left('unknown').or_else { Left('empty') } #=> Left('empty')
57
+ # Fear.right(42).or_else { Fear.right(21) } #=> Fear.right(42)
58
+ # Fear.left('unknown').or_else { Fear.right(21) } #=> Fear.right(21)
59
+ # Fear.left('unknown').or_else { Fear.left('empty') } #=> Fear.left('empty')
55
60
  #
56
61
  # @!method include?(other_value)
57
62
  # Returns +true+ if +Right+ has an element that is equal
@@ -59,9 +64,9 @@ module Fear
59
64
  # @param [any]
60
65
  # @return [Boolean]
61
66
  # @example
62
- # Right(17).include?(17) #=> true
63
- # Right(17).include?(7) #=> false
64
- # Left('undefined').include?(17) #=> false
67
+ # Fear.right(17).include?(17) #=> true
68
+ # Fear.right(17).include?(7) #=> false
69
+ # Fear.left('undefined').include?(17) #=> false
65
70
  #
66
71
  # @!method each(&block)
67
72
  # Performs the given block if this is a +Right+.
@@ -69,11 +74,11 @@ module Fear
69
74
  # @yieldreturn [void]
70
75
  # @return [Option] itself
71
76
  # @example
72
- # Right(17).each do |value|
77
+ # Fear.right(17).each do |value|
73
78
  # puts value
74
79
  # end #=> prints 17
75
80
  #
76
- # Left('undefined').each do |value|
81
+ # Fear.left('undefined').each do |value|
77
82
  # puts value
78
83
  # end #=> does nothing
79
84
  #
@@ -83,8 +88,8 @@ module Fear
83
88
  # @yieldparam [any] value
84
89
  # @yieldreturn [any]
85
90
  # @example
86
- # Right(42).map { |v| v/2 } #=> Right(21)
87
- # Left('undefined').map { |v| v/2 } #=> Left('undefined')
91
+ # Fear.right(42).map { |v| v/2 } #=> Fear.right(21)
92
+ # Fear.left('undefined').map { |v| v/2 } #=> Fear.left('undefined')
88
93
  #
89
94
  # @!method flat_map(&block)
90
95
  # Returns the given block applied to the value from this +Right+
@@ -93,24 +98,16 @@ module Fear
93
98
  # @yieldreturn [Option]
94
99
  # @return [Option]
95
100
  # @example
96
- # Right(42).flat_map { |v| Right(v/2) } #=> Right(21)
97
- # Left('undefined').flat_map { |v| Right(v/2) } #=> Left('undefined')
98
- #
99
- # @!method to_a
100
- # Returns an +Array+ containing the +Right+ value or an
101
- # empty +Array+ if this is a +Left+.
102
- # @return [Array]
103
- # @example
104
- # Right(42).to_a #=> [21]
105
- # Left('undefined').to_a #=> []
101
+ # Fear.right(42).flat_map { |v| Fear.right(v/2) } #=> Fear.right(21)
102
+ # Fear.left('undefined').flat_map { |v| Fear.right(v/2) } #=> Fear.left('undefined')
106
103
  #
107
104
  # @!method to_option
108
105
  # Returns an +Some+ containing the +Right+ value or a +None+ if
109
106
  # this is a +Left+.
110
107
  # @return [Option]
111
108
  # @example
112
- # Right(42).to_option #=> Some(21)
113
- # Left('undefined').to_option #=> None()
109
+ # Fear.right(42).to_option #=> Fear.some(21)
110
+ # Fear.left('undefined').to_option #=> Fear.none()
114
111
  #
115
112
  # @!method any?(&predicate)
116
113
  # Returns +false+ if +Left+ or returns the result of the
@@ -119,9 +116,9 @@ module Fear
119
116
  # @yieldreturn [Boolean]
120
117
  # @return [Boolean]
121
118
  # @example
122
- # Right(12).any?( |v| v > 10) #=> true
123
- # Right(7).any?( |v| v > 10) #=> false
124
- # Left('undefined').any?( |v| v > 10) #=> false
119
+ # Fear.right(12).any?( |v| v > 10) #=> true
120
+ # Fear.right(7).any?( |v| v > 10) #=> false
121
+ # Fear.left('undefined').any?( |v| v > 10) #=> false
125
122
  #
126
123
  # -----
127
124
  #
@@ -130,16 +127,16 @@ module Fear
130
127
  # @note this method is also aliased as +#success?+
131
128
  # @return [Boolean]
132
129
  # @example
133
- # Right(42).right? #=> true
134
- # Left('err').right? #=> false
130
+ # Fear.right(42).right? #=> true
131
+ # Fear.left('err').right? #=> false
135
132
  #
136
133
  # @!method left?
137
134
  # Returns +true+ if this is a +Left+, +false+ otherwise.
138
135
  # @note this method is also aliased as +#failure?+
139
136
  # @return [Boolean]
140
137
  # @example
141
- # Right(42).left? #=> false
142
- # Left('err').left? #=> true
138
+ # Fear.right(42).left? #=> false
139
+ # Fear.left('err').left? #=> true
143
140
  #
144
141
  # @!method select_or_else(default, &predicate)
145
142
  # Returns +Left+ of the default if the given predicate
@@ -149,10 +146,10 @@ module Fear
149
146
  # @yieldreturn [Boolean]
150
147
  # @return [Either]
151
148
  # @example
152
- # Right(12).select_or_else(-1, &:even?) #=> Right(12)
153
- # Right(7).select_or_else(-1, &:even?) #=> Left(-1)
154
- # Left(12).select_or_else(-1, &:even?) #=> Left(12)
155
- # Left(12).select_or_else(-> { -1 }, &:even?) #=> Left(12)
149
+ # Fear.right(12).select_or_else(-1, &:even?) #=> Fear.right(12)
150
+ # Fear.right(7).select_or_else(-1, &:even?) #=> Fear.left(-1)
151
+ # Fear.left(12).select_or_else(-1, &:even?) #=> Fear.left(12)
152
+ # Fear.left(12).select_or_else(-> { -1 }, &:even?) #=> Fear.left(12)
156
153
  #
157
154
  # @!method select(&predicate)
158
155
  # Returns +Left+ of value if the given predicate
@@ -161,10 +158,10 @@ module Fear
161
158
  # @yieldreturn [Boolean]
162
159
  # @return [Either]
163
160
  # @example
164
- # Right(12).select(&:even?) #=> Right(12)
165
- # Right(7).select(&:even?) #=> Left(7)
166
- # Left(12).select(&:even?) #=> Left(12)
167
- # Left(7).select(&:even?) #=> Left(7)
161
+ # Fear.right(12).select(&:even?) #=> Fear.right(12)
162
+ # Fear.right(7).select(&:even?) #=> Fear.left(7)
163
+ # Fear.left(12).select(&:even?) #=> Fear.left(12)
164
+ # Fear.left(7).select(&:even?) #=> Fear.left(7)
168
165
  #
169
166
  # @!method reject(&predicate)
170
167
  # Returns +Left+ of value if the given predicate holds for the
@@ -173,17 +170,17 @@ module Fear
173
170
  # @yieldreturn [Boolean]
174
171
  # @return [Either]
175
172
  # @example
176
- # Right(12).reject(&:even?) #=> Left(12)
177
- # Right(7).reject(&:even?) #=> Right(7)
178
- # Left(12).reject(&:even?) #=> Left(12)
179
- # Left(7).reject(&:even?) #=> Left(7)
173
+ # Fear.right(12).reject(&:even?) #=> Fear.left(12)
174
+ # Fear.right(7).reject(&:even?) #=> Fear.right(7)
175
+ # Fear.left(12).reject(&:even?) #=> Fear.left(12)
176
+ # Fear.left(7).reject(&:even?) #=> Fear.left(7)
180
177
  #
181
178
  # @!method swap
182
179
  # If this is a +Left+, then return the left value in +Right+ or vice versa.
183
180
  # @return [Either]
184
181
  # @example
185
- # Left('left').swap #=> Right('left')
186
- # Right('right').swap #=> Light('left')
182
+ # Fear.left('left').swap #=> Fear.right('left')
183
+ # Fear.right('right').swap #=> Fear.left('left')
187
184
  #
188
185
  # @!method reduce(reduce_left, reduce_right)
189
186
  # Applies +reduce_left+ if this is a +Left+ or +reduce_right+ if
@@ -209,10 +206,10 @@ module Fear
209
206
  # @return [Either]
210
207
  # @raise [TypeError] if it does not contain +Either+.
211
208
  # @example
212
- # Right(Right(12)).join_right #=> Right(12)
213
- # Right(Left("flower")).join_right #=> Left("flower")
214
- # Left("flower").join_right #=> Left("flower")
215
- # Left(Right("flower")).join_right #=> Left(Right("flower"))
209
+ # Fear.right(Fear.right(12)).join_right #=> Fear.right(12)
210
+ # Fear.right(Fear.left("flower")).join_right #=> Fear.left("flower")
211
+ # Fear.left("flower").join_right #=> Fear.left("flower")
212
+ # Fear.left(Fear.right("flower")).join_right #=> Fear.left(Fear.right("flower"))
216
213
  #
217
214
  # @!method join_right
218
215
  # Joins an +Either+ through +Left+. This method requires
@@ -222,16 +219,31 @@ module Fear
222
219
  # @return [Either]
223
220
  # @raise [TypeError] if it does not contain +Either+.
224
221
  # @example
225
- # Left(Right("flower")).join_left #=> Right("flower")
226
- # Left(Left(12)).join_left #=> Left(12)
227
- # Right("daisy").join_left #=> Right("daisy")
228
- # Right(Left("daisy")).join_left #=> Right(Left("daisy"))
222
+ # Fear.left(Fear.right("flower")).join_left #=> Fear.right("flower")
223
+ # Fear.left(Fear.left(12)).join_left #=> Fear.left(12)
224
+ # Fear.right("daisy").join_left #=> Fear.right("daisy")
225
+ # Fear.right(Fear.left("daisy")).join_left #=> Fear.right(Fear.left("daisy"))
226
+ #
227
+ # @!method match(&matcher)
228
+ # Pattern match against this +Either+
229
+ # @yield matcher [Fear::EitherPatternMatch]
230
+ # @example
231
+ # either.match do |m|
232
+ # m.right(Integer) do |x|
233
+ # x * 2
234
+ # end
235
+ #
236
+ # m.right(String) do |x|
237
+ # x.to_i * 2
238
+ # end
239
+ #
240
+ # m.left { |x| x }
241
+ # m.else { 'something unexpected' }
242
+ # end
229
243
  #
230
244
  # @see https://github.com/scala/scala/blob/2.12.x/src/library/scala/util/Either.scala
231
245
  #
232
246
  module Either
233
- include Dry::Equalizer(:value)
234
-
235
247
  # @private
236
248
  def left_class
237
249
  Left
@@ -249,6 +261,48 @@ module Fear
249
261
  attr_reader :value
250
262
  protected :value
251
263
 
264
+ # @param other [Any]
265
+ # @return [Boolean]
266
+ def ==(other)
267
+ other.is_a?(self.class) && value == other.value
268
+ end
269
+
270
+ # @return [String]
271
+ def inspect
272
+ "#<#{self.class} value=#{value.inspect}>"
273
+ end
274
+
275
+ # @return [String]
276
+ alias to_s inspect
277
+
278
+ # @return [<any>]
279
+ def deconstruct
280
+ [value]
281
+ end
282
+
283
+ class << self
284
+ # Build pattern matcher to be used later, despite off
285
+ # +Either#match+ method, id doesn't apply matcher immanently,
286
+ # but build it instead. Unusually in sake of efficiency it's better
287
+ # to statically build matcher and reuse it later.
288
+ #
289
+ # @example
290
+ # matcher =
291
+ # Either.matcher do |m|
292
+ # m.right(Integer, ->(x) { x > 2 }) { |x| x * 2 }
293
+ # m.right(String) { |x| x.to_i * 2 }
294
+ # m.left(String) { :err }
295
+ # m.else { 'error '}
296
+ # end
297
+ # matcher.call(Fear.right(42))
298
+ #
299
+ # @yieldparam [Fear::EitherPatternMatch]
300
+ # @return [Fear::PartialFunction]
301
+ def matcher(&matcher)
302
+ EitherPatternMatch.new(&matcher)
303
+ end
304
+ end
305
+
252
306
  # Include this mixin to access convenient factory methods.
253
307
  # @example
254
308
  # include Fear::Either::Mixin
@@ -257,16 +311,22 @@ module Fear
257
311
  # Left('beaf') #=> #<Fear::Legt value='beaf'>
258
312
  #
259
313
  module Mixin
260
- # @param [any]
261
- # @return [Left]
314
+ # @param value [any]
315
+ # @return [Fear::Left]
316
+ # @example
317
+ # Left(42) #=> #<Fear::Left value=42>
318
+ #
262
319
  def Left(value)
263
- Left.new(value)
320
+ Fear.left(value)
264
321
  end
265
322
 
266
- # @param [any]
267
- # @return [Right]
323
+ # @param value [any]
324
+ # @return [Fear::Right]
325
+ # @example
326
+ # Right(42) #=> #<Fear::Right value=42>
327
+ #
268
328
  def Right(value)
269
- Right.new(value)
329
+ Fear.right(value)
270
330
  end
271
331
  end
272
332
  end