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
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ # rubocop: disable Layout/LineLength
5
+ module FutureApi
6
+ # Asynchronously evaluates the block
7
+ # @param options [Hash] options will be passed directly to underlying +Concurrent::Promise+
8
+ # @see https://ruby-concurrency.github.io/concurrent-ruby/1.1.5/Concurrent/Promise.html#constructor_details Constructor Details
9
+ # @return [Fear::Future]
10
+ #
11
+ # @example
12
+ # require 'open-uri'
13
+ # f = Fear.future(executor: :io) { open('http://example.com') }
14
+ # f.map(&:read).each { |body| puts body }
15
+ #
16
+ def future(options = {}, &block)
17
+ Future.new(options, &block)
18
+ end
19
+ end
20
+ # rubocop: enable Layout/LineLength
21
+ end
@@ -1,7 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  class Left
3
5
  include Either
4
6
  include RightBiased::Left
7
+ include LeftPatternMatch.mixin
8
+
9
+ EXTRACTOR = proc do |either|
10
+ if Fear::Left === either
11
+ Fear.some([either.left_value])
12
+ else
13
+ Fear.none
14
+ end
15
+ end
16
+ public_constant :EXTRACTOR
17
+
18
+ # @api private
19
+ def left_value
20
+ value
21
+ end
5
22
 
6
23
  # @return [false]
7
24
  def right?
@@ -37,8 +54,8 @@ module Fear
37
54
 
38
55
  # @param reduce_left [Proc]
39
56
  # @return [any]
40
- def reduce(reduce_left, _)
41
- reduce_left.call(value)
57
+ def reduce(reduce_left, _reduce_right)
58
+ reduce_left.(value)
42
59
  end
43
60
 
44
61
  # @return [self]
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ # @api private
5
+ class LeftPatternMatch < Fear::EitherPatternMatch
6
+ def right(*)
7
+ self
8
+ end
9
+ alias success right
10
+ end
11
+ end
@@ -1,12 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
- class None
4
+ # @api private
5
+ class NoneClass
3
6
  include Option
4
- include Dry::Equalizer()
5
7
  include RightBiased::Left
8
+ include NonePatternMatch.mixin
9
+
10
+ EXTRACTOR = proc do |option|
11
+ if Fear::None === option
12
+ Fear.some([])
13
+ else
14
+ Fear.none
15
+ end
16
+ end
17
+ public_constant :EXTRACTOR
6
18
 
7
19
  # @raise [NoSuchElementError]
8
20
  def get
9
- fail NoSuchElementError
21
+ raise NoSuchElementError
10
22
  end
11
23
 
12
24
  # @return [nil]
@@ -28,5 +40,57 @@ module Fear
28
40
  def reject(*)
29
41
  self
30
42
  end
43
+
44
+ # @return [String]
45
+ def inspect
46
+ "#<Fear::NoneClass>"
47
+ end
48
+
49
+ # @return [String]
50
+ alias to_s inspect
51
+
52
+ # @param other [Any]
53
+ # @return [Boolean]
54
+ def ==(other)
55
+ other.is_a?(NoneClass)
56
+ end
57
+
58
+ # @param other
59
+ # @return [Boolean]
60
+ def ===(other)
61
+ self == other
62
+ end
63
+
64
+ # @param other [Fear::Option]
65
+ # @return [Fear::Option]
66
+ def zip(other)
67
+ if other.is_a?(Option)
68
+ Fear.none
69
+ else
70
+ raise TypeError, "can't zip with #{other.class}"
71
+ end
72
+ end
73
+
74
+ # @return [RightBiased::Left]
75
+ def filter_map
76
+ self
77
+ end
78
+ end
79
+
80
+ private_constant(:NoneClass)
81
+
82
+ # The only instance of NoneClass
83
+ # @api private
84
+ None = NoneClass.new.freeze
85
+ public_constant :None
86
+
87
+ class << NoneClass
88
+ def new
89
+ None
90
+ end
91
+
92
+ def inherited(*)
93
+ raise "you are not allowed to inherit from NoneClass, use Fear::None instead"
94
+ end
31
95
  end
32
96
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ # @api private
5
+ class NonePatternMatch < OptionPatternMatch
6
+ # @param conditions [<#==>]
7
+ # @return [Fear::OptionPatternMatch]
8
+ def some(*_conditions)
9
+ self
10
+ end
11
+ end
12
+
13
+ private_constant :NonePatternMatch
14
+ end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # Represents optional values. Instances of +Option+
3
5
  # are either an instance of +Some+ or the object +None+.
4
6
  #
5
7
  # @example The most idiomatic way to use an +Option+ instance is to treat it as a collection
6
- # name = Option(params[:name])
8
+ # name = Fear.option(params[:name])
7
9
  # upper = name.map(&:strip).select { |n| n.length != 0 }.map(&:upcase)
8
10
  # puts upper.get_or_else('')
9
11
  #
@@ -11,16 +13,13 @@ module Fear
11
13
  # having to check for the existence of a value.
12
14
  #
13
15
  # @example A less-idiomatic way to use +Option+ values is via pattern matching
14
- # name = Option(params[:name])
15
- # case name
16
- # when Some
17
- # puts name.strip.upcase
18
- # when None
19
- # puts 'No name value'
16
+ # Fear.option(params[:name]).match do |m|
17
+ # m.some { |name| name.strip.upcase }
18
+ # m.none { 'No name value' }
20
19
  # end
21
20
  #
22
21
  # @example or manually checking for non emptiness
23
- # name = Option(params[:name])
22
+ # name = Fear.option(params[:name])
24
23
  # if name.empty?
25
24
  # puts 'No name value'
26
25
  # else
@@ -34,21 +33,21 @@ module Fear
34
33
  # @yieldreturn [any]
35
34
  # @return [any]
36
35
  # @example
37
- # Some(42).get_or_else { 24/2 } #=> 42
38
- # None().get_or_else { 24/2 } #=> 12
36
+ # Fear.some(42).get_or_else { 24/2 } #=> 42
37
+ # Fear.none.get_or_else { 24/2 } #=> 12
39
38
  # @overload get_or_else(default)
40
39
  # @return [any]
41
40
  # @example
42
- # Some(42).get_or_else(12) #=> 42
43
- # None().get_or_else(12) #=> 12
41
+ # Fear.some(42).get_or_else(12) #=> 42
42
+ # Fear.none.get_or_else(12) #=> 12
44
43
  #
45
44
  # @!method or_else(&alternative)
46
45
  # Returns this +Some+ or the given alternative if this is a +None+.
47
46
  # @return [Option]
48
47
  # @example
49
- # Some(42).or_else { Some(21) } #=> Some(42)
50
- # None().or_else { Some(21) } #=> Some(21)
51
- # None().or_else { None() } #=> None()
48
+ # Fear.some(42).or_else { Fear.some(21) } #=> Fear.some(42)
49
+ # Fear.none.or_else { Fear.some(21) } #=> Fear.some(21)
50
+ # Fear.none.or_else { None } #=> None
52
51
  #
53
52
  # @!method include?(other_value)
54
53
  # Returns +true+ if it has an element that is equal
@@ -56,9 +55,9 @@ module Fear
56
55
  # @param [any]
57
56
  # @return [Boolean]
58
57
  # @example
59
- # Some(17).include?(17) #=> true
60
- # Some(17).include?(7) #=> false
61
- # None().include?(17) #=> false
58
+ # Fear.some(17).include?(17) #=> true
59
+ # Fear.some(17).include?(7) #=> false
60
+ # Fear.none.include?(17) #=> false
62
61
  #
63
62
  # @!method each(&block)
64
63
  # Performs the given block if this is a +Some+.
@@ -66,11 +65,11 @@ module Fear
66
65
  # @yieldreturn [void]
67
66
  # @return [Option] itself
68
67
  # @example
69
- # Some(17).each do |value|
68
+ # Fear.some(17).each do |value|
70
69
  # puts value
71
70
  # end #=> prints 17
72
71
  #
73
- # None().each do |value|
72
+ # Fear.none.each do |value|
74
73
  # puts value
75
74
  # end #=> does nothing
76
75
  #
@@ -80,8 +79,19 @@ module Fear
80
79
  # @yieldparam [any] value
81
80
  # @yieldreturn [any]
82
81
  # @example
83
- # Some(42).map { |v| v/2 } #=> Some(21)
84
- # None().map { |v| v/2 } #=> None()
82
+ # Fear.some(42).map { |v| v/2 } #=> Fear.some(21)
83
+ # Fear.none.map { |v| v/2 } #=> None
84
+ #
85
+ # @!method filter_map(&block)
86
+ # Returns a new +Some+ of truthy results (everything except +false+ or +nil+) of
87
+ # running the block or +None+ otherwise.
88
+ # @yieldparam [any] value
89
+ # @yieldreturn [any]
90
+ # @example
91
+ # Fear.some(42).filter_map { |v| v/2 if v.even? } #=> Fear.some(21)
92
+ # Fear.some(42).filter_map { |v| v/2 if v.odd? } #=> Fear.none
93
+ # Fear.some(42).filter_map { |v| false } #=> Fear.none
94
+ # Fear.none.filter_map { |v| v/2 } #=> Fear.none
85
95
  #
86
96
  # @!method flat_map(&block)
87
97
  # Returns the given block applied to the value from this +Some+
@@ -90,16 +100,8 @@ module Fear
90
100
  # @yieldreturn [Option]
91
101
  # @return [Option]
92
102
  # @example
93
- # Some(42).flat_map { |v| Some(v/2) } #=> Some(21)
94
- # None().flat_map { |v| Some(v/2) } #=> None()
95
- #
96
- # @!method to_a
97
- # Returns an +Array+ containing the +Some+ value or an
98
- # empty +Array+ if this is a +None+
99
- # @return [Array]
100
- # @example
101
- # Some(42).to_a #=> [21]
102
- # None().to_a #=> []
103
+ # Fear.some(42).flat_map { |v| Fear.some(v/2) } #=> Fear.some(21)
104
+ # Fear.none.flat_map { |v| Fear.some(v/2) } #=> None
103
105
  #
104
106
  # @!method any?(&predicate)
105
107
  # Returns +false+ if +None+ or returns the result of the
@@ -108,9 +110,9 @@ module Fear
108
110
  # @yieldreturn [Boolean]
109
111
  # @return [Boolean]
110
112
  # @example
111
- # Some(12).any?( |v| v > 10) #=> true
112
- # Some(7).any?( |v| v > 10) #=> false
113
- # None().any?( |v| v > 10) #=> false
113
+ # Fear.some(12).any?( |v| v > 10) #=> true
114
+ # Fear.some(7).any?( |v| v > 10) #=> false
115
+ # Fear.none.any?( |v| v > 10) #=> false
114
116
  #
115
117
  # @!method select(&predicate)
116
118
  # Returns self if it is nonempty and applying the predicate to this
@@ -119,9 +121,9 @@ module Fear
119
121
  # @yieldreturn [Boolean]
120
122
  # @return [Option]
121
123
  # @example
122
- # Some(42).select { |v| v > 40 } #=> Success(21)
123
- # Some(42).select { |v| v < 40 } #=> None()
124
- # None().select { |v| v < 40 } #=> None()
124
+ # Fear.some(42).select { |v| v > 40 } #=> Fear.success(21)
125
+ # Fear.some(42).select { |v| v < 40 } #=> None
126
+ # Fear.none.select { |v| v < 40 } #=> None
125
127
  #
126
128
  # @!method reject(&predicate)
127
129
  # Returns +Some+ if applying the predicate to this
@@ -130,9 +132,9 @@ module Fear
130
132
  # @yieldreturn [Boolean]
131
133
  # @return [Option]
132
134
  # @example
133
- # Some(42).reject { |v| v > 40 } #=> None
134
- # Some(42).reject { |v| v < 40 } #=> Some(42)
135
- # None().reject { |v| v < 40 } #=> None
135
+ # Fear.some(42).reject { |v| v > 40 } #=> None
136
+ # Fear.some(42).reject { |v| v < 40 } #=> Fear.some(42)
137
+ # Fear.none.reject { |v| v < 40 } #=> None
136
138
  #
137
139
  # @!method get
138
140
  # @return [any] the +Option+'s value.
@@ -142,15 +144,43 @@ module Fear
142
144
  # Returns +true+ if the +Option+ is +None+, +false+ otherwise.
143
145
  # @return [Boolean]
144
146
  # @example
145
- # Some(42).empty? #=> false
146
- # None().empty? #=> true
147
+ # Fear.some(42).empty? #=> false
148
+ # Fear.none.empty? #=> true
149
+ #
150
+ # @!method match(&matcher)
151
+ # Pattern match against this +Option+
152
+ # @yield matcher [Fear::OptionPatternMatch]
153
+ # @example
154
+ # Fear.option(val).match do |m|
155
+ # m.some(Integer) do |x|
156
+ # x * 2
157
+ # end
158
+ #
159
+ # m.some(String) do |x|
160
+ # x.to_i * 2
161
+ # end
162
+ #
163
+ # m.none { 'NaN' }
164
+ # m.else { 'error '}
165
+ # end
166
+ #
167
+ # @!method zip(other)
168
+ # @param other [Fear::Option]
169
+ # @return [Fear::Option] a +Fear::Some+ formed from this option and another option by
170
+ # combining the corresponding elements in an array.
171
+ #
172
+ # @example
173
+ # Fear.some("foo").zip(Fear.some("bar")) #=> Fear.some(["foo", "bar"])
174
+ # Fear.some("foo").zip(Fear.some("bar")) { |x, y| x + y } #=> Fear.some("foobar")
175
+ # Fear.some("foo").zip(Fear.none) #=> Fear.none
176
+ # Fear.none.zip(Fear.some("bar")) #=> Fear.none
147
177
  #
148
178
  # @see https://github.com/scala/scala/blob/2.11.x/src/library/scala/Option.scala
149
179
  #
150
180
  module Option
151
181
  # @private
152
182
  def left_class
153
- None
183
+ NoneClass
154
184
  end
155
185
 
156
186
  # @private
@@ -158,38 +188,72 @@ module Fear
158
188
  Some
159
189
  end
160
190
 
191
+ class << self
192
+ # Build pattern matcher to be used later, despite off
193
+ # +Option#match+ method, it doesn't apply matcher immanently,
194
+ # but build it instead. Usually in sake of efficiency it's better
195
+ # to statically build matcher and reuse it later.
196
+ #
197
+ # @example
198
+ # matcher =
199
+ # Option.matcher do |m|
200
+ # m.some(Integer) { |x| x * 2 }
201
+ # m.some(String) { |x| x.to_i * 2 }
202
+ # m.none { 'NaN' }
203
+ # m.else { 'error '}
204
+ # end
205
+ # matcher.call(Fear.some(42))
206
+ #
207
+ # @yieldparam [OptionPatternMatch]
208
+ # @return [Fear::PartialFunction]
209
+ def matcher(&matcher)
210
+ OptionPatternMatch.new(&matcher)
211
+ end
212
+
213
+ def match(value, &block)
214
+ matcher(&block).(value)
215
+ end
216
+ end
217
+
161
218
  # Include this mixin to access convenient factory methods.
162
219
  # @example
163
220
  # include Fear::Option::Mixin
164
221
  #
165
- # Option(17) #=> #<Fear::Some value=17>
222
+ # Option(17) #=> #<Fear::Some get=17>
166
223
  # Option(nil) #=> #<Fear::None>
167
- # Some(17) #=> #<Fear::Some value=17>
168
- # None() #=> #<Fear::None>
224
+ # Some(17) #=> #<Fear::Some get=17>
225
+ # None() #=> #<Fear::None>
169
226
  #
170
227
  module Mixin
171
228
  # An +Option+ factory which creates +Some+ if the argument is
172
229
  # not +nil+, and +None+ if it is +nil+.
173
230
  # @param value [any]
174
- # @return [Some, None]
231
+ # @return [Fear::Some, Fear::None]
232
+ #
233
+ # @example
234
+ # Option(17) #=> #<Fear::Some get=17>
235
+ # Option(nil) #=> #<Fear::None>
175
236
  #
176
237
  def Option(value)
177
- if value.nil?
178
- None()
179
- else
180
- Some(value)
181
- end
238
+ Fear.option(value)
182
239
  end
183
240
 
184
241
  # @return [None]
242
+ # @example
243
+ # None() #=> #<Fear::None>
244
+ #
185
245
  def None
186
- None.new
246
+ Fear.none
187
247
  end
188
248
 
189
249
  # @param value [any] except nil
190
- # @return [None]
250
+ # @return [Fear::Some]
251
+ # @example
252
+ # Some(17) #=> #<Fear::Some get=17>
253
+ # Some(nil) #=> #<Fear::Some get=nil>
254
+ #
191
255
  def Some(value)
192
- Some.new(value)
256
+ Fear.some(value)
193
257
  end
194
258
  end
195
259
  end