fear 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +27 -0
- data/.github/workflows/rubocop.yml +39 -0
- data/.github/workflows/spec.yml +42 -0
- data/.rubocop.yml +4 -60
- data/.simplecov +17 -0
- data/CHANGELOG.md +29 -1
- data/Gemfile +5 -5
- data/Gemfile.lock +86 -50
- data/README.md +240 -209
- data/Rakefile +72 -65
- data/examples/pattern_extracting.rb +10 -8
- data/examples/pattern_matching_binary_tree_set.rb +7 -2
- data/examples/pattern_matching_number_in_words.rb +48 -42
- data/fear.gemspec +33 -34
- data/lib/dry/types/fear/option.rb +125 -0
- data/lib/dry/types/fear.rb +8 -0
- data/lib/fear/await.rb +33 -0
- data/lib/fear/awaitable.rb +28 -0
- data/lib/fear/either.rb +15 -4
- data/lib/fear/either_api.rb +4 -0
- data/lib/fear/either_pattern_match.rb +9 -5
- data/lib/fear/empty_partial_function.rb +3 -1
- data/lib/fear/failure.rb +7 -7
- data/lib/fear/failure_pattern_match.rb +4 -0
- data/lib/fear/for.rb +4 -2
- data/lib/fear/for_api.rb +5 -1
- data/lib/fear/future.rb +157 -82
- data/lib/fear/future_api.rb +17 -4
- data/lib/fear/left.rb +3 -9
- data/lib/fear/left_pattern_match.rb +2 -0
- data/lib/fear/none.rb +28 -10
- data/lib/fear/none_pattern_match.rb +2 -0
- data/lib/fear/option.rb +30 -2
- data/lib/fear/option_api.rb +4 -0
- data/lib/fear/option_pattern_match.rb +8 -3
- data/lib/fear/partial_function/and_then.rb +4 -2
- data/lib/fear/partial_function/any.rb +2 -0
- data/lib/fear/partial_function/combined.rb +3 -1
- data/lib/fear/partial_function/empty.rb +6 -0
- data/lib/fear/partial_function/guard/and.rb +2 -0
- data/lib/fear/partial_function/guard/and3.rb +2 -0
- data/lib/fear/partial_function/guard/or.rb +2 -0
- data/lib/fear/partial_function/guard.rb +8 -6
- data/lib/fear/partial_function/lifted.rb +2 -0
- data/lib/fear/partial_function/or_else.rb +5 -1
- data/lib/fear/partial_function.rb +18 -9
- data/lib/fear/partial_function_class.rb +3 -1
- data/lib/fear/pattern_match.rb +3 -11
- data/lib/fear/pattern_matching_api.rb +6 -28
- data/lib/fear/promise.rb +7 -5
- data/lib/fear/right.rb +3 -9
- data/lib/fear/right_biased.rb +5 -3
- data/lib/fear/right_pattern_match.rb +4 -0
- data/lib/fear/some.rb +35 -8
- data/lib/fear/some_pattern_match.rb +2 -0
- data/lib/fear/struct.rb +237 -0
- data/lib/fear/success.rb +7 -8
- data/lib/fear/success_pattern_match.rb +4 -0
- data/lib/fear/try.rb +8 -2
- data/lib/fear/try_api.rb +4 -0
- data/lib/fear/try_pattern_match.rb +9 -5
- data/lib/fear/unit.rb +6 -2
- data/lib/fear/utils.rb +14 -2
- data/lib/fear/version.rb +4 -1
- data/lib/fear.rb +26 -44
- data/spec/dry/types/fear/option/constrained_spec.rb +22 -0
- data/spec/dry/types/fear/option/core_spec.rb +77 -0
- data/spec/dry/types/fear/option/default_spec.rb +21 -0
- data/spec/dry/types/fear/option/hash_spec.rb +58 -0
- data/spec/dry/types/fear/option/option_spec.rb +97 -0
- data/spec/fear/awaitable_spec.rb +19 -0
- data/spec/fear/done_spec.rb +7 -5
- data/spec/fear/either/mixin_spec.rb +4 -2
- data/spec/fear/either_pattern_match_spec.rb +10 -8
- data/spec/fear/either_pattern_matching_spec.rb +28 -0
- data/spec/fear/either_spec.rb +26 -0
- data/spec/fear/failure_spec.rb +57 -70
- data/spec/fear/for/mixin_spec.rb +15 -0
- data/spec/fear/for_spec.rb +19 -17
- data/spec/fear/future_spec.rb +477 -237
- data/spec/fear/guard_spec.rb +136 -24
- data/spec/fear/left_spec.rb +57 -70
- data/spec/fear/none_spec.rb +39 -43
- data/spec/fear/option/mixin_spec.rb +9 -7
- data/spec/fear/option_pattern_match_spec.rb +10 -8
- data/spec/fear/option_pattern_matching_spec.rb +34 -0
- data/spec/fear/option_spec.rb +142 -0
- data/spec/fear/partial_function/any_spec.rb +25 -0
- data/spec/fear/partial_function/empty_spec.rb +12 -10
- data/spec/fear/partial_function_and_then_spec.rb +39 -37
- data/spec/fear/partial_function_composition_spec.rb +46 -44
- data/spec/fear/partial_function_or_else_spec.rb +92 -90
- data/spec/fear/partial_function_spec.rb +91 -61
- data/spec/fear/pattern_match_spec.rb +19 -51
- data/spec/fear/pattern_matching_api_spec.rb +31 -0
- data/spec/fear/promise_spec.rb +23 -23
- data/spec/fear/right_biased/left.rb +28 -26
- data/spec/fear/right_biased/right.rb +51 -49
- data/spec/fear/right_spec.rb +48 -68
- data/spec/fear/some_spec.rb +30 -40
- data/spec/fear/success_spec.rb +40 -60
- data/spec/fear/try/mixin_spec.rb +19 -3
- data/spec/fear/try_api_spec.rb +23 -0
- data/spec/fear/try_pattern_match_spec.rb +10 -8
- data/spec/fear/try_pattern_matching_spec.rb +34 -0
- data/spec/fear/utils_spec.rb +16 -14
- data/spec/spec_helper.rb +13 -7
- data/spec/struct_pattern_matching_spec.rb +36 -0
- data/spec/struct_spec.rb +194 -0
- data/spec/support/dry_types.rb +6 -0
- metadata +128 -87
- data/.travis.yml +0 -13
- data/lib/fear/extractor/anonymous_array_splat_matcher.rb +0 -8
- data/lib/fear/extractor/any_matcher.rb +0 -15
- data/lib/fear/extractor/array_head_matcher.rb +0 -34
- data/lib/fear/extractor/array_matcher.rb +0 -38
- data/lib/fear/extractor/array_splat_matcher.rb +0 -14
- data/lib/fear/extractor/empty_list_matcher.rb +0 -18
- data/lib/fear/extractor/extractor_matcher.rb +0 -42
- data/lib/fear/extractor/grammar.rb +0 -201
- data/lib/fear/extractor/grammar.treetop +0 -129
- data/lib/fear/extractor/identifier_matcher.rb +0 -16
- data/lib/fear/extractor/matcher/and.rb +0 -36
- data/lib/fear/extractor/matcher.rb +0 -54
- data/lib/fear/extractor/named_array_splat_matcher.rb +0 -15
- data/lib/fear/extractor/pattern.rb +0 -55
- data/lib/fear/extractor/typed_identifier_matcher.rb +0 -24
- data/lib/fear/extractor/value_matcher.rb +0 -17
- data/lib/fear/extractor.rb +0 -108
- data/lib/fear/extractor_api.rb +0 -33
- data/spec/fear/extractor/array_matcher_spec.rb +0 -228
- data/spec/fear/extractor/extractor_matcher_spec.rb +0 -151
- data/spec/fear/extractor/grammar_array_spec.rb +0 -23
- data/spec/fear/extractor/identified_matcher_spec.rb +0 -47
- data/spec/fear/extractor/identifier_matcher_spec.rb +0 -66
- data/spec/fear/extractor/pattern_spec.rb +0 -32
- data/spec/fear/extractor/typed_identifier_matcher_spec.rb +0 -62
- data/spec/fear/extractor/value_matcher_number_spec.rb +0 -77
- data/spec/fear/extractor/value_matcher_string_spec.rb +0 -86
- data/spec/fear/extractor/value_matcher_symbol_spec.rb +0 -69
- data/spec/fear/extractor_api_spec.rb +0 -113
- 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
|
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 [
|
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 [
|
97
|
-
# @return [
|
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(
|
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"
|
data/lib/fear/either_api.rb
CHANGED
@@ -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,
|
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,
|
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
|
-
|
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
|
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
|
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.
|
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.
|
67
|
+
Fear::For.(monads, &block)
|
64
68
|
end
|
65
69
|
end
|
66
70
|
end
|