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,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ module PartialFunction
5
+ class Guard
6
+ # @api private
7
+ class And < Guard
8
+ # @param c1 [Fear::PartialFunction::Guard]
9
+ # @param c2 [Fear::PartialFunction::Guard]
10
+ def initialize(c1, c2)
11
+ @c1 = c1
12
+ @c2 = c2
13
+ end
14
+ attr_reader :c1, :c2
15
+ private :c1
16
+ private :c2
17
+
18
+ # @param other [Fear::PartialFunction::Guard]
19
+ # @return [Fear::PartialFunction::Guard]
20
+ def and(other)
21
+ Guard::And.new(self, other)
22
+ end
23
+
24
+ # @param other [Fear::PartialFunction::Guard]
25
+ # @return [Fear::PartialFunction::Guard]
26
+ def or(other)
27
+ Guard::Or.new(self, other)
28
+ end
29
+
30
+ # @param arg [any]
31
+ # @return [Boolean]
32
+ def ===(arg)
33
+ (c1 === arg) && (c2 === arg)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ module PartialFunction
5
+ class Guard
6
+ # @api private
7
+ class And3 < Guard
8
+ # @param c1 [#===]
9
+ # @param c2 [#===]
10
+ # @param c3 [#===]
11
+ def initialize(c1, c2, c3)
12
+ @c1 = c1
13
+ @c2 = c2
14
+ @c3 = c3
15
+ end
16
+ attr_reader :c1, :c2, :c3
17
+ private :c1
18
+ private :c2
19
+ private :c3
20
+
21
+ # @param other [Fear::PartialFunction::Guard]
22
+ # @return [Fear::PartialFunction::Guard]
23
+ def and(other)
24
+ Guard::And.new(self, other)
25
+ end
26
+
27
+ # @param other [Fear::PartialFunction::Guard]
28
+ # @return [Fear::PartialFunction::Guard]
29
+ def or(other)
30
+ Guard::Or.new(self, other)
31
+ end
32
+
33
+ # @param arg [any]
34
+ # @return [Boolean]
35
+ def ===(arg)
36
+ (c1 === arg) && (c2 === arg) && (c3 === arg)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ module PartialFunction
5
+ class Guard
6
+ # @api private
7
+ class Or < Guard
8
+ # @param c1 [Fear::PartialFunction::Guard]
9
+ # @param c2 [Fear::PartialFunction::Guard]
10
+ def initialize(c1, c2)
11
+ @c1 = c1
12
+ @c2 = c2
13
+ end
14
+ attr_reader :c1, :c2
15
+ private :c1
16
+ private :c2
17
+
18
+ # @param other [Fear::PartialFunction::Guard]
19
+ # @return [Fear::PartialFunction::Guard]
20
+ def and(other)
21
+ Guard::And.new(self, other)
22
+ end
23
+
24
+ # @param other [Fear::PartialFunction::Guard]
25
+ # @return [Fear::PartialFunction::Guard]
26
+ def or(other)
27
+ Guard::Or.new(self, other)
28
+ end
29
+
30
+ # @param arg [any]
31
+ # @return [Boolean]
32
+ def ===(arg)
33
+ (c1 === arg) || (c2 === arg)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ module PartialFunction
5
+ # @api private
6
+ class Lifted
7
+ # @param pf [Fear::PartialFunction]
8
+ def initialize(pf)
9
+ @pf = pf
10
+ end
11
+ attr_reader :pf
12
+ private :pf
13
+
14
+ # @param arg [any]
15
+ # @return [Fear::Option]
16
+ def call(arg)
17
+ Some.new(pf.call_or_else(arg) { return Fear::None })
18
+ end
19
+ end
20
+
21
+ private_constant :Lifted
22
+ end
23
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ module PartialFunction
5
+ # Composite function produced by +PartialFunction#or_else+ method
6
+ # @api private
7
+ class OrElse
8
+ include PartialFunction
9
+
10
+ # @param f1 [Fear::PartialFunction]
11
+ # @param f2 [Fear::PartialFunction]
12
+ def initialize(f1, f2)
13
+ @f1 = f1
14
+ @f2 = f2
15
+ end
16
+ # @!attribute f1
17
+ # @return [Fear::PartialFunction]
18
+ # @!attribute f2
19
+ # @return [Fear::PartialFunction]
20
+ attr_reader :f1, :f2
21
+ private :f1
22
+ private :f2
23
+
24
+ # @param arg [any]
25
+ # @return [any]
26
+ def call(arg)
27
+ f1.call_or_else(arg, &f2)
28
+ end
29
+
30
+ alias === call
31
+ alias [] call
32
+
33
+ # @param other [Fear::PartialFunction]
34
+ # @return [Fear::PartialFunction]
35
+ def or_else(other)
36
+ OrElse.new(f1, f2.or_else(other))
37
+ end
38
+
39
+ # @see Fear::PartialFunction#and_then
40
+ def and_then(other = Utils::UNDEFINED, &block)
41
+ Utils.with_block_or_argument("Fear::PartialFunction::OrElse#and_then", other, block) do |fun|
42
+ OrElse.new(f1.and_then(&fun), f2.and_then(&fun))
43
+ end
44
+ end
45
+
46
+ # @param arg [any]
47
+ # @return [Boolean]
48
+ def defined_at?(arg)
49
+ f1.defined_at?(arg) || f2.defined_at?(arg)
50
+ end
51
+
52
+ # @param arg [any]
53
+ # @param fallback [Proc]
54
+ # @return [any]
55
+ def call_or_else(arg, &fallback)
56
+ f1.call_or_else(arg) do
57
+ return f2.call_or_else(arg, &fallback)
58
+ end
59
+ end
60
+ end
61
+
62
+ private_constant :OrElse
63
+ end
64
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ # @api private
5
+ class PartialFunctionClass
6
+ include PartialFunction
7
+
8
+ # @param condition [#call] describes the domain of partial function
9
+ # @param function [Proc] function definition
10
+ def initialize(condition, &function)
11
+ @condition = condition
12
+ @function = function
13
+ end
14
+ attr_reader :condition, :function
15
+ private :condition
16
+ private :function
17
+
18
+ # @param arg [any]
19
+ # @return [any] Calls this partial function with the given argument when it
20
+ # is contained in the function domain.
21
+ # @raise [MatchError] when this partial function is not defined.
22
+ def call(arg)
23
+ call_or_else(arg, &PartialFunction::EMPTY)
24
+ end
25
+
26
+ # @param arg [any]
27
+ # @yield [arg] if function not defined
28
+ def call_or_else(arg)
29
+ if defined_at?(arg)
30
+ function.(arg)
31
+ else
32
+ yield arg
33
+ end
34
+ end
35
+ end
36
+
37
+ private_constant :PartialFunctionClass
38
+ end
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ # Pattern match builder. Provides DSL for building pattern matcher
5
+ # Pattern match is just a combination of partial functions
6
+ #
7
+ # matcher = Fear.matcher do |m|
8
+ # m.case(Integer) { |x| x * 2 }
9
+ # m.case(String) { |x| x.to_i(10) * 2 }
10
+ # end
11
+ # matcher.is_a?(Fear::PartialFunction) #=> true
12
+ # matcher.defined_at?(4) #=> true
13
+ # matcher.defined_at?('4') #=> true
14
+ # matcher.defined_at?(nil) #=> false
15
+ #
16
+ # The previous example is the same as:
17
+ #
18
+ # Fear.case(Integer) { |x| x * ) }
19
+ # .or_else(
20
+ # Fear.case(String) { |x| x.to_i(10) * 2 }
21
+ # )
22
+ #
23
+ # You can provide +else+ branch, so partial function will be defined
24
+ # on any input:
25
+ #
26
+ # matcher = Fear.matcher do |m|
27
+ # m.else { 'Match' }
28
+ # end
29
+ # matcher.call(42) #=> 'Match'
30
+ #
31
+ # @see Fear.matcher public interface for building matchers
32
+ # @api Fear
33
+ # @note Use this class only to build custom pattern match classes. See +Fear::OptionPatternMatch+ as an example.
34
+ class PatternMatch
35
+ class << self
36
+ alias __new__ new
37
+
38
+ # @return [Fear::PartialFunction]
39
+ def new
40
+ builder = __new__(PartialFunction::EMPTY)
41
+ yield builder
42
+ builder.result
43
+ end
44
+
45
+ # Creates anonymous module to add `#mathing` behaviour to a class
46
+ #
47
+ # @example
48
+ # class User
49
+ # include Fear::PatternMatch.mixin
50
+ # end
51
+ #
52
+ # user.match do |m\
53
+ # m.case(:admin?) { |u| ... }
54
+ # m.else { |u| ... }
55
+ # end
56
+ #
57
+ # @param as [Symbol, String] (:match) method name
58
+ # @return [Module]
59
+ def mixin(as: :match)
60
+ matcher_class = self
61
+
62
+ Module.new do
63
+ define_method(as) do |&matchers|
64
+ matcher_class.new(&matchers).(self)
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ # @param result [Fear::EmptyPartialFunction]
71
+ def initialize(result)
72
+ @result = result
73
+ end
74
+ attr_accessor :result
75
+ private :result=
76
+
77
+ # @see Fear::PartialFunction#else
78
+ def else(&effect)
79
+ or_else(Fear.case(&effect))
80
+ end
81
+
82
+ # This method is syntactic sugar for `PartialFunction#or_else`, but rather than passing
83
+ # another partial function as an argument, you pass arguments to build such partial function.
84
+ # @example This two examples produces the same result
85
+ # other = Fear.case(Integer) { |x| x * 2 }
86
+ # this.or_else(other)
87
+ #
88
+ # this.case(Integer) { |x| x * 2 }
89
+ #
90
+ # @param guards [<#===>]
91
+ # @param effect [Proc]
92
+ # @return [Fear::PartialFunction]
93
+ # @see #or_else for details
94
+ def case(*guards, &effect)
95
+ or_else(Fear.case(*guards, &effect))
96
+ end
97
+
98
+ # @param pattern [String]
99
+ # @param guards [<#===>]
100
+ # @param effect [Proc]
101
+ # @return [Fear::PartialFunction]
102
+ # @see #case for details
103
+ # @see Fear.xcase for details
104
+ def xcase(pattern, *guards, &effect)
105
+ or_else(Fear.xcase(pattern, *guards, &effect))
106
+ end
107
+
108
+ # @see Fear::PartialFunction#or_else
109
+ def or_else(other)
110
+ self.result = result.or_else(other)
111
+ self
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,137 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fear
4
+ # @api private
5
+ module PatternMatchingApi
6
+ # Creates pattern match. Use `case` method to
7
+ # define matching branches. Branch consist of a
8
+ # guardian, which describes domain of the
9
+ # branch and function to apply to matching value.
10
+ #
11
+ # @example This mather apply different functions to Integers and to Strings
12
+ # matcher = Fear.matcher do |m|
13
+ # m.case(Integer) { |n| "#{n} is a number" }
14
+ # m.case(String) { |n| "#{n} is a string" }
15
+ # end
16
+ #
17
+ # matcher.(42) #=> "42 is a number"
18
+ # matcher.("Foo") #=> "Foo is a string"
19
+ #
20
+ # if you pass something other than Integer or string, it will raise `Fear::MatchError`:
21
+ #
22
+ # matcher.(10..20) #=> raises Fear::MatchError
23
+ #
24
+ # to avoid raising `MatchError`, you can use `else` method. It defines a branch matching
25
+ # on any value.
26
+ #
27
+ # matcher = Fear.matcher do |m|
28
+ # m.case(Integer) { |n| "#{n} is a number" }
29
+ # m.case(String) { |n| "#{n} is a string" }
30
+ # m.else { |n| "#{n} is a #{n.class}" }
31
+ # end
32
+ #
33
+ # matcher.(10..20) #=> "10..20 is a Range"
34
+ #
35
+ # You can use anything as a guardian if it responds to `#===` method:
36
+ #
37
+ # m.case(20..40) { |m| "#{m} is within range" }
38
+ # m.case(->(x) { x > 10}) { |m| "#{m} is greater than 10" }
39
+ # m.case(:even?.to_proc) { |x| "#{x} is even" }
40
+ # m.case(:odd?.to_proc) { |x| "#{x} is odd" }
41
+ #
42
+ # It's also possible to pass several guardians. All should match to pass
43
+ #
44
+ # m.case(Integer, :even?.to_proc) { |x| ... }
45
+ # m.case(Integer, :odd?.to_proc) { |x| ... }
46
+ #
47
+ # If you want to perform pattern destruction, use +#xcase+ method
48
+ #
49
+ # m.xcase('Date(year, 12, 31)') { |year:| "Last day of the year #{year}" }
50
+ #
51
+ # The pattern above ensures that it's 31 of December and extracts year to block named parameter
52
+ #
53
+ # Since matcher returns +Fear::PartialFunction+, you can combine matchers using
54
+ # partial function API:
55
+ #
56
+ # failures = Fear.matcher do |m|
57
+ # m.case('not_found') { ... }
58
+ # m.case('network_error') { ... }
59
+ # end
60
+ #
61
+ # success = Fear.matcher do |m|
62
+ # m.case('ok') { ... }
63
+ # end
64
+ #
65
+ # response = failures.or_else(success)
66
+ #
67
+ # @yieldparam matcher [Fear::PatternMatch]
68
+ # @return [Fear::PartialFunction]
69
+ # @see Fear::OptionPatternMatch for example of custom matcher
70
+ def matcher(&block)
71
+ PatternMatch.new(&block)
72
+ end
73
+
74
+ # Pattern match against given value
75
+ #
76
+ # @example
77
+ # Fear.match(42) do |m|
78
+ # m.case(Integer, :even?.to_proc) { |n| "#{n} is even number" }
79
+ # m.case(Integer, :odd?.to_proc) { |n| "#{n} is odd number" }
80
+ # m.case(Strings) { |n| "#{n} is a string" }
81
+ # m.else { 'unknown' }
82
+ # end #=> "42 is even number"
83
+ #
84
+ # @param value [any]
85
+ # @yieldparam matcher [Fear::PartialFunction]
86
+ # @return [any]
87
+ def match(value, &block)
88
+ matcher(&block).(value)
89
+ end
90
+
91
+ # Creates partial function defined on domain described with guards
92
+ #
93
+ # @example
94
+ # pf = Fear.case(Integer) { |x| x / 2 }
95
+ # pf.defined_at?(4) #=> true
96
+ # pf.defined_at?('Foo') #=> false
97
+ #
98
+ # @example multiple guards combined using logical "and"
99
+ # pf = Fear.case(Integer, :even?.to_proc) { |x| x / 2 }
100
+ # pf.defined_at?(4) #=> true
101
+ # pf.defined_at?(3) #=> false
102
+ #
103
+ # @note to make more complex matches, you are encouraged to use Qo gem.
104
+ # @see Qo https://github.com/baweaver/qo
105
+ # @example
106
+ # Fear.case(Qo[age: 20..30]) { |_| 'old enough' }
107
+ #
108
+ # @param guards [<#===>]
109
+ # @param function [Proc]
110
+ # @return [Fear::PartialFunction]
111
+ def case(*guards, &function)
112
+ PartialFunction.and(*guards, &function)
113
+ end
114
+
115
+ # Creates partial function defined on domain described with guard
116
+ # and perform pattern extraction.
117
+ #
118
+ # @param pattern [String] pattern to match against
119
+ # @param guards [<#===>] other guards against extracted pattern
120
+ # @yieldparam hash [{Symbol => any}]
121
+ # @return [Fear::PartialFunction]
122
+ #
123
+ # @example
124
+ # pf = Fear.xcase('['ok', Some(body)]') { |body:| ... }
125
+ # pf.defined_at?(['ok', Fear.some(body)]) #=> true
126
+ # pf.defined_at?(['err', Fear.none]) #=> false
127
+ #
128
+ # @example pattern and guards. It matches against non-empty body
129
+ #
130
+ # pf = Fear.xcase('['ok', Some(body)]', ->(body:) { !body.empty? }) { }
131
+ #
132
+ def xcase(pattern, *guards, &function)
133
+ warn "NOTE: Fear.xcase is deprecated and will be removed in a future version. Use `case .. in ..` instead."
134
+ Fear[pattern].and_then(self.case(*guards, &function))
135
+ end
136
+ end
137
+ end