fear 1.0.0 → 2.0.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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/.github/dependabot.yml +27 -0
  3. data/.github/workflows/rubocop.yml +39 -0
  4. data/.github/workflows/spec.yml +42 -0
  5. data/.rubocop.yml +4 -60
  6. data/.simplecov +17 -0
  7. data/CHANGELOG.md +29 -1
  8. data/Gemfile +5 -5
  9. data/Gemfile.lock +86 -50
  10. data/README.md +240 -209
  11. data/Rakefile +72 -65
  12. data/examples/pattern_extracting.rb +10 -8
  13. data/examples/pattern_matching_binary_tree_set.rb +7 -2
  14. data/examples/pattern_matching_number_in_words.rb +48 -42
  15. data/fear.gemspec +33 -34
  16. data/lib/dry/types/fear/option.rb +125 -0
  17. data/lib/dry/types/fear.rb +8 -0
  18. data/lib/fear/await.rb +33 -0
  19. data/lib/fear/awaitable.rb +28 -0
  20. data/lib/fear/either.rb +15 -4
  21. data/lib/fear/either_api.rb +4 -0
  22. data/lib/fear/either_pattern_match.rb +9 -5
  23. data/lib/fear/empty_partial_function.rb +3 -1
  24. data/lib/fear/failure.rb +7 -7
  25. data/lib/fear/failure_pattern_match.rb +4 -0
  26. data/lib/fear/for.rb +4 -2
  27. data/lib/fear/for_api.rb +5 -1
  28. data/lib/fear/future.rb +157 -82
  29. data/lib/fear/future_api.rb +17 -4
  30. data/lib/fear/left.rb +3 -9
  31. data/lib/fear/left_pattern_match.rb +2 -0
  32. data/lib/fear/none.rb +28 -10
  33. data/lib/fear/none_pattern_match.rb +2 -0
  34. data/lib/fear/option.rb +30 -2
  35. data/lib/fear/option_api.rb +4 -0
  36. data/lib/fear/option_pattern_match.rb +8 -3
  37. data/lib/fear/partial_function/and_then.rb +4 -2
  38. data/lib/fear/partial_function/any.rb +2 -0
  39. data/lib/fear/partial_function/combined.rb +3 -1
  40. data/lib/fear/partial_function/empty.rb +6 -0
  41. data/lib/fear/partial_function/guard/and.rb +2 -0
  42. data/lib/fear/partial_function/guard/and3.rb +2 -0
  43. data/lib/fear/partial_function/guard/or.rb +2 -0
  44. data/lib/fear/partial_function/guard.rb +8 -6
  45. data/lib/fear/partial_function/lifted.rb +2 -0
  46. data/lib/fear/partial_function/or_else.rb +5 -1
  47. data/lib/fear/partial_function.rb +18 -9
  48. data/lib/fear/partial_function_class.rb +3 -1
  49. data/lib/fear/pattern_match.rb +3 -11
  50. data/lib/fear/pattern_matching_api.rb +6 -28
  51. data/lib/fear/promise.rb +7 -5
  52. data/lib/fear/right.rb +3 -9
  53. data/lib/fear/right_biased.rb +5 -3
  54. data/lib/fear/right_pattern_match.rb +4 -0
  55. data/lib/fear/some.rb +35 -8
  56. data/lib/fear/some_pattern_match.rb +2 -0
  57. data/lib/fear/struct.rb +237 -0
  58. data/lib/fear/success.rb +7 -8
  59. data/lib/fear/success_pattern_match.rb +4 -0
  60. data/lib/fear/try.rb +8 -2
  61. data/lib/fear/try_api.rb +4 -0
  62. data/lib/fear/try_pattern_match.rb +9 -5
  63. data/lib/fear/unit.rb +6 -2
  64. data/lib/fear/utils.rb +14 -2
  65. data/lib/fear/version.rb +4 -1
  66. data/lib/fear.rb +26 -44
  67. data/spec/dry/types/fear/option/constrained_spec.rb +22 -0
  68. data/spec/dry/types/fear/option/core_spec.rb +77 -0
  69. data/spec/dry/types/fear/option/default_spec.rb +21 -0
  70. data/spec/dry/types/fear/option/hash_spec.rb +58 -0
  71. data/spec/dry/types/fear/option/option_spec.rb +97 -0
  72. data/spec/fear/awaitable_spec.rb +19 -0
  73. data/spec/fear/done_spec.rb +7 -5
  74. data/spec/fear/either/mixin_spec.rb +4 -2
  75. data/spec/fear/either_pattern_match_spec.rb +10 -8
  76. data/spec/fear/either_pattern_matching_spec.rb +28 -0
  77. data/spec/fear/either_spec.rb +26 -0
  78. data/spec/fear/failure_spec.rb +57 -70
  79. data/spec/fear/for/mixin_spec.rb +15 -0
  80. data/spec/fear/for_spec.rb +19 -17
  81. data/spec/fear/future_spec.rb +477 -237
  82. data/spec/fear/guard_spec.rb +136 -24
  83. data/spec/fear/left_spec.rb +57 -70
  84. data/spec/fear/none_spec.rb +39 -43
  85. data/spec/fear/option/mixin_spec.rb +9 -7
  86. data/spec/fear/option_pattern_match_spec.rb +10 -8
  87. data/spec/fear/option_pattern_matching_spec.rb +34 -0
  88. data/spec/fear/option_spec.rb +142 -0
  89. data/spec/fear/partial_function/any_spec.rb +25 -0
  90. data/spec/fear/partial_function/empty_spec.rb +12 -10
  91. data/spec/fear/partial_function_and_then_spec.rb +39 -37
  92. data/spec/fear/partial_function_composition_spec.rb +46 -44
  93. data/spec/fear/partial_function_or_else_spec.rb +92 -90
  94. data/spec/fear/partial_function_spec.rb +91 -61
  95. data/spec/fear/pattern_match_spec.rb +19 -51
  96. data/spec/fear/pattern_matching_api_spec.rb +31 -0
  97. data/spec/fear/promise_spec.rb +23 -23
  98. data/spec/fear/right_biased/left.rb +28 -26
  99. data/spec/fear/right_biased/right.rb +51 -49
  100. data/spec/fear/right_spec.rb +48 -68
  101. data/spec/fear/some_spec.rb +30 -40
  102. data/spec/fear/success_spec.rb +40 -60
  103. data/spec/fear/try/mixin_spec.rb +19 -3
  104. data/spec/fear/try_api_spec.rb +23 -0
  105. data/spec/fear/try_pattern_match_spec.rb +10 -8
  106. data/spec/fear/try_pattern_matching_spec.rb +34 -0
  107. data/spec/fear/utils_spec.rb +16 -14
  108. data/spec/spec_helper.rb +13 -7
  109. data/spec/struct_pattern_matching_spec.rb +36 -0
  110. data/spec/struct_spec.rb +194 -0
  111. data/spec/support/dry_types.rb +6 -0
  112. metadata +128 -87
  113. data/.travis.yml +0 -13
  114. data/lib/fear/extractor/anonymous_array_splat_matcher.rb +0 -8
  115. data/lib/fear/extractor/any_matcher.rb +0 -15
  116. data/lib/fear/extractor/array_head_matcher.rb +0 -34
  117. data/lib/fear/extractor/array_matcher.rb +0 -38
  118. data/lib/fear/extractor/array_splat_matcher.rb +0 -14
  119. data/lib/fear/extractor/empty_list_matcher.rb +0 -18
  120. data/lib/fear/extractor/extractor_matcher.rb +0 -42
  121. data/lib/fear/extractor/grammar.rb +0 -201
  122. data/lib/fear/extractor/grammar.treetop +0 -129
  123. data/lib/fear/extractor/identifier_matcher.rb +0 -16
  124. data/lib/fear/extractor/matcher/and.rb +0 -36
  125. data/lib/fear/extractor/matcher.rb +0 -54
  126. data/lib/fear/extractor/named_array_splat_matcher.rb +0 -15
  127. data/lib/fear/extractor/pattern.rb +0 -55
  128. data/lib/fear/extractor/typed_identifier_matcher.rb +0 -24
  129. data/lib/fear/extractor/value_matcher.rb +0 -17
  130. data/lib/fear/extractor.rb +0 -108
  131. data/lib/fear/extractor_api.rb +0 -33
  132. data/spec/fear/extractor/array_matcher_spec.rb +0 -228
  133. data/spec/fear/extractor/extractor_matcher_spec.rb +0 -151
  134. data/spec/fear/extractor/grammar_array_spec.rb +0 -23
  135. data/spec/fear/extractor/identified_matcher_spec.rb +0 -47
  136. data/spec/fear/extractor/identifier_matcher_spec.rb +0 -66
  137. data/spec/fear/extractor/pattern_spec.rb +0 -32
  138. data/spec/fear/extractor/typed_identifier_matcher_spec.rb +0 -62
  139. data/spec/fear/extractor/value_matcher_number_spec.rb +0 -77
  140. data/spec/fear/extractor/value_matcher_string_spec.rb +0 -86
  141. data/spec/fear/extractor/value_matcher_symbol_spec.rb +0 -69
  142. data/spec/fear/extractor_api_spec.rb +0 -113
  143. data/spec/fear/extractor_spec.rb +0 -59
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dry
4
+ module Types
5
+ class Option
6
+ include Type
7
+ include ::Dry::Equalizer(:type, :options, inspect: false, immutable: true)
8
+ include Decorator
9
+ include Builder
10
+ include Printable
11
+
12
+ # @param [Fear::Option, Object] input
13
+ #
14
+ # @return [Fear::Option]
15
+ #
16
+ # @api private
17
+ def call_unsafe(input = Undefined)
18
+ case input
19
+ when ::Fear::Option
20
+ input
21
+ when Undefined
22
+ Fear.none
23
+ else
24
+ Fear.option(type.call_unsafe(input))
25
+ end
26
+ end
27
+
28
+ # @param [Fear::Option, Object] input
29
+ #
30
+ # @return [Fear::Option]
31
+ #
32
+ # @api private
33
+ def call_safe(input = Undefined)
34
+ case input
35
+ when ::Fear::Option
36
+ input
37
+ when Undefined
38
+ Fear.none
39
+ else
40
+ Fear.option(type.call_safe(input) { |output = input| return yield(output) })
41
+ end
42
+ end
43
+
44
+ # @param [Object] input
45
+ #
46
+ # @return [Result::Success]
47
+ #
48
+ # @api public
49
+ def try(input = Undefined)
50
+ result = type.try(input)
51
+
52
+ if result.success?
53
+ Result::Success.new(Fear.option(result.input))
54
+ else
55
+ result
56
+ end
57
+ end
58
+
59
+ # @return [true]
60
+ #
61
+ # @api public
62
+ def default?
63
+ true
64
+ end
65
+
66
+ # @param [Object] value
67
+ #
68
+ # @see Dry::Types::Builder#default
69
+ #
70
+ # @raise [ArgumentError] if nil provided as default value
71
+ #
72
+ # @api public
73
+ def default(value)
74
+ if value.nil?
75
+ raise ArgumentError, "nil cannot be used as a default of a maybe type"
76
+ else
77
+ super
78
+ end
79
+ end
80
+ end
81
+
82
+ module Builder
83
+ # Turn a type into a maybe type
84
+ #
85
+ # @return [Option]
86
+ #
87
+ # @api public
88
+ def option
89
+ Option.new(Types["nil"] | self)
90
+ end
91
+ end
92
+
93
+ # @api private
94
+ class Schema
95
+ class Key
96
+ # @api private
97
+ def option
98
+ __new__(type.option)
99
+ end
100
+ end
101
+ end
102
+
103
+ # @api private
104
+ class Printer
105
+ MAPPING[Option] = :visit_option
106
+
107
+ # @api private
108
+ def visit_option(maybe)
109
+ visit(maybe.type) do |type|
110
+ yield "Fear::Option<#{type}>"
111
+ end
112
+ end
113
+ end
114
+
115
+ # Register non-coercible maybe types
116
+ NON_NIL.each_key do |name|
117
+ register("option.strict.#{name}", self[name.to_s].option)
118
+ end
119
+
120
+ # Register coercible maybe types
121
+ COERCIBLE.each_key do |name|
122
+ register("option.coercible.#{name}", self["coercible.#{name}"].option)
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/types"
4
+ require "fear"
5
+
6
+ Dry::Types.register_extension(:fear_option) do
7
+ require "dry/types/fear/option"
8
+ end
data/lib/fear/await.rb ADDED
@@ -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
data/lib/fear/either.rb CHANGED
@@ -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+.
@@ -70,7 +72,7 @@ module Fear
70
72
  # Performs the given block if this is a +Right+.
71
73
  # @yieldparam [any] value
72
74
  # @yieldreturn [void]
73
- # @return [Option] itself
75
+ # @return [Fear::Either] itself
74
76
  # @example
75
77
  # Fear.right(17).each do |value|
76
78
  # puts value
@@ -93,8 +95,8 @@ module Fear
93
95
  # Returns the given block applied to the value from this +Right+
94
96
  # or returns this if this is a +Left+.
95
97
  # @yieldparam [any] value
96
- # @yieldreturn [Option]
97
- # @return [Option]
98
+ # @yieldreturn [Fear::Either]
99
+ # @return [Fear::Either]
98
100
  # @example
99
101
  # Fear.right(42).flat_map { |v| Fear.right(v/2) } #=> Fear.right(21)
100
102
  # Fear.left('undefined').flat_map { |v| Fear.right(v/2) } #=> Fear.left('undefined')
@@ -104,7 +106,7 @@ module Fear
104
106
  # this is a +Left+.
105
107
  # @return [Option]
106
108
  # @example
107
- # Fear.right(42).to_option #=> Fear.some(21)
109
+ # Fear.right(42).to_option #=> Fear.some(42)
108
110
  # Fear.left('undefined').to_option #=> Fear.none()
109
111
  #
110
112
  # @!method any?(&predicate)
@@ -273,6 +275,11 @@ module Fear
273
275
  # @return [String]
274
276
  alias to_s inspect
275
277
 
278
+ # @return [<any>]
279
+ def deconstruct
280
+ [value]
281
+ end
282
+
276
283
  class << self
277
284
  # Build pattern matcher to be used later, despite off
278
285
  # +Either#match+ method, id doesn't apply matcher immanently,
@@ -324,3 +331,7 @@ module Fear
324
331
  end
325
332
  end
326
333
  end
334
+
335
+ require "fear/either_pattern_match"
336
+ require "fear/left"
337
+ require "fear/right"
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fear/either"
4
+
1
5
  module Fear
2
6
  module EitherApi
3
7
  # @param value [any]
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fear/pattern_match"
4
+
1
5
  module Fear
2
6
  # Either pattern matcher
3
7
  #
@@ -22,15 +26,12 @@ module Fear
22
26
  # @note it has two optimized subclasses +Fear::LeftPatternMatch+ and +Fear::RightPatternMatch+
23
27
  # @api private
24
28
  class EitherPatternMatch < Fear::PatternMatch
25
- LEFT_EXTRACTOR = :left_value.to_proc
26
- RIGHT_EXTRACTOR = :right_value.to_proc
27
-
28
29
  # Match against +Fear::Right+
29
30
  #
30
31
  # @param conditions [<#==>]
31
32
  # @return [Fear::EitherPatternMatch]
32
33
  def right(*conditions, &effect)
33
- branch = Fear.case(Fear::Right, &RIGHT_EXTRACTOR).and_then(Fear.case(*conditions, &effect))
34
+ branch = Fear.case(Fear::Right, &:right_value).and_then(Fear.case(*conditions, &effect))
34
35
  or_else(branch)
35
36
  end
36
37
  alias success right
@@ -40,9 +41,12 @@ module Fear
40
41
  # @param conditions [<#==>]
41
42
  # @return [Fear::EitherPatternMatch]
42
43
  def left(*conditions, &effect)
43
- branch = Fear.case(Fear::Left, &LEFT_EXTRACTOR).and_then(Fear.case(*conditions, &effect))
44
+ branch = Fear.case(Fear::Left, &:left_value).and_then(Fear.case(*conditions, &effect))
44
45
  or_else(branch)
45
46
  end
46
47
  alias failure left
47
48
  end
48
49
  end
50
+
51
+ require "fear/left_pattern_match"
52
+ require "fear/right_pattern_match"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # Use singleton version of EmptyPartialFunction -- PartialFunction::EMPTY
3
5
  # @api private
@@ -28,7 +30,7 @@ module Fear
28
30
  end
29
31
 
30
32
  def to_s
31
- 'Empty partial function'
33
+ "Empty partial function"
32
34
  end
33
35
  end
34
36
 
data/lib/fear/failure.rb CHANGED
@@ -1,15 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  class Failure
3
5
  include Try
4
6
  include RightBiased::Left
5
7
  include FailurePatternMatch.mixin
6
- EXTRACTOR = proc do |try|
7
- if Fear::Failure === try
8
- Fear.some([try.exception])
9
- else
10
- Fear.none
11
- end
12
- end
13
8
 
14
9
  # @param [StandardError]
15
10
  def initialize(exception)
@@ -101,5 +96,10 @@ module Fear
101
96
 
102
97
  # @return [String]
103
98
  alias to_s inspect
99
+
100
+ # @return [<StandardError>]
101
+ def deconstruct
102
+ [exception]
103
+ end
104
104
  end
105
105
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Fear
2
4
  # @api private
3
5
  class FailurePatternMatch < Fear::TryPatternMatch
@@ -5,4 +7,6 @@ module Fear
5
7
  self
6
8
  end
7
9
  end
10
+
11
+ private_constant :FailurePatternMatch
8
12
  end
data/lib/fear/for.rb CHANGED
@@ -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
data/lib/fear/for_api.rb CHANGED
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fear/for"
4
+
1
5
  module Fear
2
6
  module ForApi
3
7
  # Syntactic sugar for composition of multiple monadic operations. It supports two such
@@ -60,7 +64,7 @@ module Fear
60
64
  # @return [{#map, #flat_map}]
61
65
  #
62
66
  def for(*monads, &block)
63
- Fear::For.call(monads, &block)
67
+ Fear::For.(monads, &block)
64
68
  end
65
69
  end
66
70
  end