fear 1.0.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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