mona-result 0.1.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 170acae8e3dab4e7c9dec3b88b17cb8668a678290aafa5eb363da54261a5eab9
4
+ data.tar.gz: 0b327151acf8487019f87f5b859b1f940d766e67c84479c29ef1a70a4d26aeb1
5
+ SHA512:
6
+ metadata.gz: 45ca05476a2621a4d6e76a190b37852880b9c69d43470ec77ed5dc9dabf7ec9f6cbe9771777adc369f69ce421e8297f2e5c5ee66df8bf43b12c62d1e0ce3c53d
7
+ data.tar.gz: 59684cbea28ce2392a1755edb70c9756786b1bf730900e5aaf0829a3a4a068a0742c6db907e3720d4533ff17ff57a2c7b5a72e7dd5d1364c4acfd4f6a4bafd80
data/.rubocop.yml ADDED
@@ -0,0 +1,20 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.1
3
+ NewCops: enable
4
+
5
+ Style/StringLiterals:
6
+ Enabled: true
7
+ EnforcedStyle: double_quotes
8
+
9
+ Style/StringLiteralsInInterpolation:
10
+ Enabled: true
11
+ EnforcedStyle: double_quotes
12
+
13
+ Style/NumberedParametersLimit:
14
+ Max: 2
15
+
16
+ Naming/MethodParameterName:
17
+ MinNameLength: 2
18
+
19
+ Layout/LineLength:
20
+ Max: 120
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-08-04
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in mona-result.gemspec
6
+ gemspec
7
+
8
+ gem "minitest"
9
+ gem "rake"
10
+ gem "rubocop"
11
+ gem "rubocop-minitest"
12
+ gem "rubocop-rake"
13
+ gem "steep"
data/Gemfile.lock ADDED
@@ -0,0 +1,82 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mona-result (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activesupport (7.0.3.1)
10
+ concurrent-ruby (~> 1.0, >= 1.0.2)
11
+ i18n (>= 1.6, < 2)
12
+ minitest (>= 5.1)
13
+ tzinfo (~> 2.0)
14
+ ast (2.4.2)
15
+ concurrent-ruby (1.1.10)
16
+ ffi (1.15.5)
17
+ i18n (1.12.0)
18
+ concurrent-ruby (~> 1.0)
19
+ json (2.6.2)
20
+ language_server-protocol (3.16.0.3)
21
+ listen (3.7.1)
22
+ rb-fsevent (~> 0.10, >= 0.10.3)
23
+ rb-inotify (~> 0.9, >= 0.9.10)
24
+ minitest (5.16.2)
25
+ parallel (1.22.1)
26
+ parser (3.1.2.1)
27
+ ast (~> 2.4.1)
28
+ rainbow (3.1.1)
29
+ rake (13.0.6)
30
+ rb-fsevent (0.11.1)
31
+ rb-inotify (0.10.1)
32
+ ffi (~> 1.0)
33
+ rbs (2.6.0)
34
+ regexp_parser (2.5.0)
35
+ rexml (3.2.5)
36
+ rubocop (1.33.0)
37
+ json (~> 2.3)
38
+ parallel (~> 1.10)
39
+ parser (>= 3.1.0.0)
40
+ rainbow (>= 2.2.2, < 4.0)
41
+ regexp_parser (>= 1.8, < 3.0)
42
+ rexml (>= 3.2.5, < 4.0)
43
+ rubocop-ast (>= 1.19.1, < 2.0)
44
+ ruby-progressbar (~> 1.7)
45
+ unicode-display_width (>= 1.4.0, < 3.0)
46
+ rubocop-ast (1.21.0)
47
+ parser (>= 3.1.1.0)
48
+ rubocop-minitest (0.21.0)
49
+ rubocop (>= 0.90, < 2.0)
50
+ rubocop-rake (0.6.0)
51
+ rubocop (~> 1.0)
52
+ ruby-progressbar (1.11.0)
53
+ steep (1.1.1)
54
+ activesupport (>= 5.1)
55
+ language_server-protocol (>= 3.15, < 4.0)
56
+ listen (~> 3.0)
57
+ parallel (>= 1.0.0)
58
+ parser (>= 3.1)
59
+ rainbow (>= 2.2.2, < 4.0)
60
+ rbs (>= 2.3.2)
61
+ terminal-table (>= 2, < 4)
62
+ terminal-table (3.0.2)
63
+ unicode-display_width (>= 1.1.1, < 3)
64
+ tzinfo (2.0.5)
65
+ concurrent-ruby (~> 1.0)
66
+ unicode-display_width (2.2.0)
67
+
68
+ PLATFORMS
69
+ x86_64-darwin-21
70
+ x86_64-linux
71
+
72
+ DEPENDENCIES
73
+ minitest
74
+ mona-result!
75
+ rake
76
+ rubocop
77
+ rubocop-minitest
78
+ rubocop-rake
79
+ steep
80
+
81
+ BUNDLED WITH
82
+ 2.3.19
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Ian White
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # Mona::Result
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/mona/result`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Install the gem and add to the application's Gemfile by executing:
10
+
11
+ $ bundle add mona-result
12
+
13
+ If bundler is not being used to manage dependencies, install the gem by executing:
14
+
15
+ $ gem install mona-result
16
+
17
+ ## Usage
18
+
19
+ TODO: Write usage instructions here
20
+
21
+ ## Development
22
+
23
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
24
+
25
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
26
+
27
+ ## Contributing
28
+
29
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/mona-result.
30
+
31
+ ## License
32
+
33
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ task :steep do
7
+ system "steep check" or raise "Steep type checking failed"
8
+ end
9
+
10
+ Rake::TestTask.new(:test) do |t|
11
+ t.libs << "test"
12
+ t.libs << "lib"
13
+ t.test_files = FileList["test/**/test_*.rb"]
14
+ end
15
+
16
+ require "rubocop/rake_task"
17
+
18
+ RuboCop::RakeTask.new
19
+
20
+ task default: %i[steep test rubocop]
data/Steepfile ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ D = Steep::Diagnostic
4
+
5
+ target :lib do
6
+ signature "sig"
7
+
8
+ check "lib"
9
+
10
+ configure_code_diagnostics(D::Ruby.default)
11
+ end
12
+
13
+ target :test do
14
+ signature "sig"
15
+
16
+ check "test"
17
+
18
+ library "minitest", "mutex_m"
19
+
20
+ configure_code_diagnostics(D::Ruby.strict)
21
+ configure_code_diagnostics do |h|
22
+ h[D::Ruby::UnsupportedSyntax] = :information
23
+ end
24
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mona
4
+ module Result
5
+ # mixin for objects that return Result::Dict results
6
+ #
7
+ # define #perform to do the work, use #set to add results to the Result::Dict, the first failure will abort the
8
+ # rest of the #perform method and the result will be returned. This works like monadic 'do' notation.
9
+ #
10
+ # After a result is set at a key, it can be accessed via its key name as a method.
11
+ #
12
+ # use the Action with #call
13
+ #
14
+ # example:
15
+ #
16
+ # class UpdateUser
17
+ # inlcude Mona::Result::Action
18
+ # include Auditing # for example, adds #audit(model, input) method
19
+ #
20
+ # def perform(user_id, attributes)
21
+ # set :user, UserRepo.find(user_id) # if find is Err, the block exits with failure of :user
22
+ # set :input, UserInput.valid(attributes) # likewise if valid is Err, the block exits
23
+ # set [:user, :input], UserRepo.update(user.id, input) # note that #input and #user are available methods
24
+ # # if update is Err it is set on the :input key
25
+ # audit(user, input) # this only runs if all of the above set successful results
26
+ # end
27
+ # end
28
+ #
29
+ # UpdateUser.new.call(user_id, attributes) # => Result
30
+ module Action
31
+ # You can create an Ephemeral Action as follows:
32
+ #
33
+ # Example:
34
+ # compute = Mona::Result::Action::Ephemeral.new do |x, y|
35
+ # set :numerator, x
36
+ # puts "set numerator: #{numerator}"
37
+ # set :denominator, y.zero? ? Result.failure(y, :zero) : y
38
+ # puts "set denominator: #{denominator}"
39
+ # set :answer, numerator / denominator
40
+ # puts "answer: #{answer}"
41
+ # end
42
+ #
43
+ # > compute.call(10,2)
44
+ # set numerator: 10
45
+ # set denominator: 2
46
+ # answer: 5
47
+ # => #<Result success: {:numerator=>10, :denominator=>2, :answer=>5}>
48
+ #
49
+ # > compute.call(10,0)
50
+ # set numerator: 10
51
+ # => #<Result failure: {:numerator=>10, :denominator=>0}, error: {:error=>:zero, :on=>:denominator}>
52
+ #
53
+ # Mona::Result.action is a shortcut for this
54
+ class Ephemeral
55
+ include Action
56
+
57
+ def initialize(&perform)
58
+ @perform = perform
59
+ end
60
+
61
+ def perform(*args, **kwargs) = instance_exec(*args, **kwargs, &@perform)
62
+ end
63
+
64
+ def call(*args, **kwargs)
65
+ @sequence = Sequence.new
66
+ @sequence.call { |_sequence| perform(*args, **kwargs) }
67
+ ensure
68
+ remove_instance_variable :@sequence
69
+ end
70
+
71
+ private
72
+
73
+ def set(key, value) = @sequence.set(key, value)
74
+
75
+ def respond_to_missing?(key, _include_private = false) = @sequence.key?(key)
76
+
77
+ def method_missing(key, *args)
78
+ return @sequence.fetch(key) if args.empty? && @sequence.key?(key)
79
+
80
+ raise NoMethodError, "no method `#{key}' for #{self}"
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mona
4
+ module Result
5
+ # Represents a dictionary of results, it is successful if all results are successful, and a failure if one
6
+ # is a failure. A Result::Dict can only contain one failure.
7
+ class Dict
8
+ # factory method that returns {Dict::Success} or {Dict::Failure}
9
+ def self.new(initial = {}, &block)
10
+ result = Dict::EMPTY
11
+ initial.each { |k, v| result = result.set(k, v) }
12
+ result = result.sequence(&block) if block
13
+ result
14
+ end
15
+
16
+ # Dict read interface
17
+ module ReadInterface
18
+ def [](key) = to_h[key]
19
+
20
+ def key?(key) = to_h.key?(key)
21
+
22
+ def fetch(key) = to_h.fetch(key)
23
+ end
24
+
25
+ # OK dict result
26
+ class OK < Result::OK
27
+ include ReadInterface
28
+
29
+ def set(key, to_result)
30
+ key, failure_key = key if key.is_a?(Array)
31
+ failure_key ||= key
32
+
33
+ Result[to_result].either \
34
+ ->(value) { OK.new to_h.merge(key => value) },
35
+ ->(failure, reason, **m) { Err.new to_h.merge(failure_key => failure), reason, **m, key: failure_key }
36
+ end
37
+
38
+ def sequence(&) = Sequence.new(self).call(&)
39
+
40
+ def to_h = @value
41
+ end
42
+
43
+ # Err dict result
44
+ class Err < Result::Err
45
+ include ReadInterface
46
+
47
+ def set(_key, _val) = raise(Error, "cannot #set on #{self}")
48
+
49
+ def sequence(&) = self
50
+
51
+ def to_h = @failure
52
+ end
53
+
54
+ EMPTY = OK.new({}).freeze
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mona
4
+ module Result
5
+ # A Error or failure result, with optional reason, and metadata
6
+ class Err
7
+ def initialize(failure, reason = nil, **meta)
8
+ raise ArgumentError, "meta can't contain :reason key" if meta.key?(:reason)
9
+
10
+ @failure = failure.freeze
11
+ @reason = reason
12
+ @meta = meta
13
+ end
14
+
15
+ attr_reader :failure, :reason, :meta
16
+
17
+ def ok? = false
18
+
19
+ def err? = true
20
+
21
+ def value_or(&) = yield
22
+
23
+ def ok(&) = nil
24
+
25
+ def err(&) = yield @failure, @reason, **@meta
26
+
27
+ def either(_ok, err) = err.call(@failure, @reason, **@meta)
28
+
29
+ def and_then(&) = self
30
+
31
+ def and_tap(&) = self
32
+
33
+ def or_else(&) = Result[yield @failure, @reason, **@meta]
34
+
35
+ def deconstruct = [:err, @failure, @reason, @meta]
36
+
37
+ def deconstruct_keys(_keys = nil) = { err: @failure, reason: @reason, **@meta }
38
+
39
+ def to_result = self
40
+
41
+ def to_s
42
+ "#<Err #{@failure.inspect} #{{ reason: @reason, **@meta }.map{ "#{_1}: #{_2.inspect}" }.join(", ")}>"
43
+ end
44
+
45
+ alias inspect to_s
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mona
4
+ module Result
5
+ # Base error for Result
6
+ class Error < StandardError; end
7
+
8
+ # raised when Result::Match does not match the result
9
+ class NoMatchError < Error
10
+ attr_reader :result
11
+
12
+ def initialize(result)
13
+ @result = result
14
+ super("No match found for #{result}")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mona
4
+ module Result
5
+ # Use Match.call to respond to result success or failure
6
+ #
7
+ # Result::Match.call(result) do |r|
8
+ # r.ok { |value| ... }
9
+ # r.err(error, **meta) { |failure, reason, **meta| ... }
10
+ # r.err(error) { |failure, reason, **meta| ... }
11
+ # r.err { |failure, reason, **meta| ... }
12
+ # end
13
+ class Match
14
+ def self.call(result, &) = new(result).call(&)
15
+
16
+ def initialize(result)
17
+ @throw = Object.new
18
+ @result = result
19
+ end
20
+
21
+ def call
22
+ catch @throw do
23
+ yield self
24
+ raise NoMatchError, @result
25
+ end
26
+ end
27
+
28
+ def ok
29
+ @result.and_then do |value|
30
+ throw @throw, yield(value)
31
+ end
32
+ end
33
+
34
+ def err(match_reason = nil, **match_meta)
35
+ @result.or_else do |failure, reason, **meta|
36
+ if match_reason?(match_reason, reason) && match_meta?(match_meta, meta)
37
+ throw @throw, yield(failure, reason, **meta)
38
+ end
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ def match_reason?(match_reason, reason)
45
+ match_reason.nil? || match_reason === reason # rubocop:disable Style/CaseEquality
46
+ end
47
+
48
+ def match_meta?(match_meta, meta)
49
+ match_meta.all? { |key, val| val.nil? || val === meta[key] } # rubocop:disable Style/CaseEquality
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mona
4
+ module Result
5
+ # A Successful (OK) result
6
+ class OK
7
+ def initialize(value)
8
+ @value = value.freeze
9
+ end
10
+
11
+ attr_reader :value
12
+
13
+ def value_or(&) = @value
14
+
15
+ def ok? = true
16
+
17
+ def err? = false
18
+
19
+ def ok(&) = yield @value
20
+
21
+ def err(&) = nil
22
+
23
+ def either(ok, _err) = ok.call(@value)
24
+
25
+ def and_then(&) = Result[yield @value]
26
+
27
+ def and_tap(&) = tap { yield @value }
28
+
29
+ def or_else(&) = self
30
+
31
+ def deconstruct = [:ok, @value]
32
+
33
+ def deconstruct_keys(_keys = nil) = { ok: @value }
34
+
35
+ def to_result = self
36
+
37
+ def to_s = "#<OK #{@value.inspect}>"
38
+
39
+ alias inspect to_s
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mona
4
+ module Result
5
+ # Sequence.call { ... } allows monadic 'do' notation for Result::Dict, where #set-ing the first failure skips the
6
+ # remainder of the block and returns the Result::Dict
7
+ class Sequence
8
+ include Dict::ReadInterface
9
+
10
+ def self.call(result = Dict::EMPTY, &) = new(result).call(&)
11
+
12
+ def initialize(result = Dict::EMPTY)
13
+ @result = result
14
+ @throw = Object.new
15
+ end
16
+
17
+ def call
18
+ catch(@throw) do
19
+ yield self
20
+ @result
21
+ end
22
+ end
23
+
24
+ def set(key, value)
25
+ @result = @result.set(key, value)
26
+ ensure
27
+ throw @throw, @result if @result.err?
28
+ end
29
+
30
+ def to_h = @result.to_h
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Mona
4
+ module Result
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "result/version"
4
+ require_relative "result/error"
5
+
6
+ module Mona
7
+ # Monadic result
8
+ #
9
+ # @author Ian White
10
+ # @since 0.1.0
11
+ module Result
12
+ autoload :OK, "mona/result/ok.rb"
13
+ autoload :Err, "mona/result/err.rb"
14
+ autoload :Match, "mona/result/match.rb"
15
+ autoload :Dict, "mona/result/dict.rb"
16
+ autoload :Sequence, "mona/result/sequence.rb"
17
+ autoload :Action, "mona/result/action.rb"
18
+
19
+ def self.[](obj) = obj.respond_to?(:to_result) ? obj.to_result : OK.new(obj)
20
+
21
+ module_function
22
+
23
+ def to_result(obj) = Result[obj]
24
+
25
+ def ok(value) = OK.new(value)
26
+
27
+ def err(failure, reason = nil, **meta) = Err.new(failure, reason, **meta)
28
+
29
+ def on_result(result, &) = Match.call(result, &)
30
+
31
+ def on_ok(result, &) = Match.call(result) { _1.ok(&) }
32
+
33
+ def dict(initial = {}, &) = Dict.new(initial, &)
34
+
35
+ def action(&) = Action::Ephemeral.new(&)
36
+ end
37
+ end
@@ -0,0 +1,177 @@
1
+ module Mona
2
+ module Result
3
+ VERSION: String
4
+
5
+ type dict = Hash[Symbol, untyped]
6
+ type keys = Array[Symbol]
7
+ type result[T] = (_OK[T] | _Err[T])
8
+ type dict_result = result[dict] & _Dict
9
+ type set_key = (Symbol | [Symbol, Symbol])
10
+
11
+ def self.[]: (untyped) -> result[untyped]
12
+ def self?.to_result: (untyped) -> result[untyped]
13
+ def self?.ok: [T] (T) -> _OK[T]
14
+ def self?.err: [T] (T, ?untyped, **untyped) -> _Err[T]
15
+ def self?.dict: (?dict) ?{ (Sequence) -> void } -> dict_result
16
+ def self?.on_result: (result[untyped]) { (Match) -> void } -> untyped
17
+ def self?.on_ok: (result[untyped]) { (untyped) -> void } -> untyped
18
+ def self?.action: () { (*untyped) -> void } -> Action::Ephemeral
19
+
20
+ interface _OK[T]
21
+ def initialize: (T) -> void
22
+ def value: -> T
23
+ def ok?: -> true
24
+ def err?: -> false
25
+ def ok: [R] () { (T) -> R } -> R
26
+ def err: () { (T, untyped, **untyped) -> void } -> nil
27
+ def either: [R] (^(T) -> R, ^(T, untyped, **untyped) -> void) -> R
28
+ def value_or: () { -> void } -> T
29
+ def and_then: () { (T) -> untyped } -> result[untyped]
30
+ def and_tap: () { (T) -> void } -> self
31
+ def or_else: () { (T, ?untyped, **untyped) -> untyped } -> self
32
+ def deconstruct: -> [:ok, T]
33
+ def deconstruct_keys: (?keys?) -> { ok: T }
34
+ end
35
+
36
+ interface _Err[T]
37
+ def initialize: (T, ?untyped, **untyped) -> void
38
+ def failure: -> T
39
+ def reason: -> untyped
40
+ def meta: -> dict
41
+ def ok?: -> false
42
+ def err?: -> true
43
+ def ok: () { (untyped) -> void } -> nil
44
+ def err: [R] () { (T, untyped, **untyped) -> R } -> R
45
+ def either: [R] (^(T) -> void, ^(T, untyped, **untyped) -> R) -> R
46
+ def value_or: [R] () { -> R } -> R
47
+ def and_then: () { (T) -> untyped } -> self
48
+ def and_tap: () { (T) -> void } -> self
49
+ def or_else: () { (T, ?untyped, **untyped) -> untyped } -> result[untyped]
50
+ def deconstruct: -> [:err, T, untyped, dict]
51
+ def deconstruct_keys: (?keys?) -> dict
52
+ end
53
+
54
+ interface _ToH
55
+ def to_h: -> dict
56
+ end
57
+
58
+ interface _DictRead
59
+ def key? : (Symbol) -> bool
60
+ def fetch : (Symbol) -> untyped
61
+ def [] : (Symbol) -> untyped
62
+ end
63
+
64
+ interface _Dict
65
+ include _DictRead
66
+ def set: (set_key, untyped) -> dict_result
67
+ def sequence: () { (Sequence) -> void } -> dict_result
68
+ def to_h: -> dict
69
+ end
70
+
71
+ interface _Perform
72
+ def perform: (*untyped, **untyped) -> void
73
+ end
74
+
75
+ class Error < StandardError
76
+ end
77
+
78
+ class NoMatchError < Error
79
+ attr_reader result: _Err[untyped]
80
+ def initialize: (_Err[untyped]) -> void
81
+ end
82
+
83
+ class OK
84
+ include _OK[untyped]
85
+
86
+ @value: untyped
87
+ end
88
+
89
+ class Err
90
+ include _Err[untyped]
91
+
92
+ @failure: untyped
93
+ @reason: untyped
94
+ @meta: dict
95
+ end
96
+
97
+ class Dict
98
+ EMPTY: dict_result
99
+
100
+ module ReadInterface : _ToH
101
+ include _DictRead
102
+ end
103
+
104
+ class OK < Result::OK
105
+ include ReadInterface
106
+
107
+ include _OK[dict]
108
+ include _Dict
109
+
110
+ @value: dict
111
+ end
112
+
113
+ class Err < Result::Err
114
+ include ReadInterface
115
+
116
+ include _Err[dict]
117
+ include _Dict
118
+
119
+ @failure: dict
120
+ @reason: untyped
121
+ @meta: dict
122
+ end
123
+
124
+ def self.new: (?dict) ?{ (Sequence) -> void } -> dict_result
125
+ end
126
+
127
+ class Match
128
+ @throw: Object
129
+ @result: result[untyped]
130
+
131
+ def self.call: (result[untyped]) { (Match) -> void } -> untyped
132
+ def initialize: (result[untyped]) -> void
133
+ def call: () { (Match) -> void } -> untyped
134
+ def ok: () { (untyped) -> void } -> void
135
+ def err: (?untyped, **untyped) { (untyped, ?untyped, **untyped) -> void } -> void
136
+
137
+ private
138
+
139
+ def match_reason?: (untyped, untyped) -> bool
140
+ def match_meta?: (untyped, untyped) -> bool
141
+ end
142
+
143
+ class Sequence
144
+ include Dict::ReadInterface
145
+
146
+ @throw: Object
147
+ @result: dict_result
148
+
149
+ def self.call: (?dict_result) { (Sequence) -> void } -> dict_result
150
+ def initialize: (?dict_result) -> void
151
+ def call: () { (Sequence) -> void } -> dict_result
152
+ def set: (set_key, untyped) -> void
153
+ def to_h: -> dict
154
+ end
155
+
156
+ module Action : _Perform
157
+ class Ephemeral
158
+ include Action
159
+
160
+ @perform: ^(*untyped, **untyped) -> void
161
+
162
+ def initialize: () { (*untyped, **untyped) -> void } -> void
163
+ def perform: (*untyped, **untyped) -> void
164
+ end
165
+
166
+ @sequence: Sequence
167
+
168
+ def call: (*untyped, **untyped) -> dict_result
169
+
170
+ private
171
+
172
+ def set: (set_key, untyped) -> void
173
+ def respond_to_missing?: (Symbol, ?bool) -> bool
174
+ def method_missing: (Symbol, *untyped) -> untyped
175
+ end
176
+ end
177
+ end
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mona-result
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ian White
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2022-08-09 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Mona::Result provides a result monad, and dict_result monad
14
+ email:
15
+ - ian.w.white@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".rubocop.yml"
21
+ - CHANGELOG.md
22
+ - Gemfile
23
+ - Gemfile.lock
24
+ - LICENSE.txt
25
+ - README.md
26
+ - Rakefile
27
+ - Steepfile
28
+ - lib/mona/result.rb
29
+ - lib/mona/result/action.rb
30
+ - lib/mona/result/dict.rb
31
+ - lib/mona/result/err.rb
32
+ - lib/mona/result/error.rb
33
+ - lib/mona/result/match.rb
34
+ - lib/mona/result/ok.rb
35
+ - lib/mona/result/sequence.rb
36
+ - lib/mona/result/version.rb
37
+ - sig/mona/result.rbs
38
+ homepage: https://github.com/mona/mona-result
39
+ licenses:
40
+ - MIT
41
+ metadata:
42
+ rubygems_mfa_required: 'true'
43
+ homepage_uri: https://github.com/mona/mona-result
44
+ source_code_uri: https://github.com/mona/mona-result
45
+ changelog_uri: https://github.com/mona/mona-result/CHANGELOG.md
46
+ post_install_message:
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 3.1.0
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubygems_version: 3.3.7
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: Mona::Result is a result Monad for ruby
65
+ test_files: []