Skip to content

Commit

Permalink
Merge pull request #126 from bolshakov/feature/ruby-3.0
Browse files Browse the repository at this point in the history
Feature/ruby 3.0
  • Loading branch information
bolshakov committed Feb 8, 2021
2 parents 8687323 + bfd89aa commit cc40be5
Show file tree
Hide file tree
Showing 41 changed files with 125 additions and 177 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/spec.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
ruby: [ '2.5.x', '2.6.x', '2.7.x' ]
ruby: [ '2.6.x', '2.7.x' , '3.0.x']
name: ${{ matrix.ruby }}

steps:
Expand Down
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
## 1.x
## 2.0.0 (not yet released)

* Start testing against Ruby 3.0.0
* Minimal supported Ruby version is 2.6.0
* Make future and promises compatible with Ruby 3.0 method syntax.
* Get rid off autoload in favor of require. Autoload appeared to be not thread-safe.
* All monads are shareable with Ractors
* Drop pattern extraction support (`Fear.xcase`).
* Add top-level factory methods: `Fear::Some`, `Fear::Option`, `Fear::Right`, `Fear::Left`,
`Fear::Try`, `Fear::Success`, and `Fear::Failure`.
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ GEM
any (0.1.0)
ast (2.4.1)
benchmark-ips (2.8.4)
concurrent-ruby (1.1.7)
concurrent-ruby (1.1.8)
diff-lcs (1.4.4)
docile (1.3.4)
dry-configurable (0.11.6)
Expand Down
1 change: 1 addition & 0 deletions fear.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^bin\/}) { |f| File.basename(f) }
spec.test_files = spec.files.grep(%r{^spec\/})
spec.require_paths = ["lib"]
spec.required_ruby_version = Gem::Requirement.new(">= 2.6.0")

spec.add_development_dependency "benchmark-ips"
spec.add_development_dependency "bundler"
Expand Down
43 changes: 4 additions & 39 deletions lib/fear.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# frozen_string_literal: true

require "fear/utils"
require "fear/right_biased"
require "fear/struct"
require "fear/unit"
require "fear/either_api"
require "fear/for_api"
require "fear/future_api"
Expand Down Expand Up @@ -31,45 +35,6 @@ module Fear
extend PatternMatchingApi
extend TryApi

autoload :EmptyPartialFunction, "fear/empty_partial_function"
autoload :PartialFunction, "fear/partial_function"
autoload :PartialFunctionClass, "fear/partial_function_class"
autoload :PatternMatch, "fear/pattern_match"

autoload :Unit, "fear/unit"
autoload :For, "fear/for"
autoload :RightBiased, "fear/right_biased"
autoload :Utils, "fear/utils"

autoload :None, "fear/none"
autoload :NoneClass, "fear/none"
autoload :NonePatternMatch, "fear/none_pattern_match"
autoload :Option, "fear/option"
autoload :OptionPatternMatch, "fear/option_pattern_match"
autoload :Some, "fear/some"
autoload :SomePatternMatch, "fear/some_pattern_match"

autoload :Failure, "fear/failure"
autoload :FailurePatternMatch, "fear/failure_pattern_match"
autoload :Success, "fear/success"
autoload :SuccessPatternMatch, "fear/success_pattern_match"
autoload :Try, "fear/try"
autoload :TryPatternMatch, "fear/try_pattern_match"

autoload :Either, "fear/either"
autoload :EitherPatternMatch, "fear/either_pattern_match"
autoload :Left, "fear/left"
autoload :LeftPatternMatch, "fear/left_pattern_match"
autoload :Right, "fear/right"
autoload :RightPatternMatch, "fear/right_pattern_match"

autoload :Await, "fear/await"
autoload :Awaitable, "fear/awaitable"
autoload :Future, "fear/future"
autoload :Promise, "fear/promise"

autoload :Struct, "fear/struct"

module Mixin
include Either::Mixin
include For::Mixin
Expand Down
4 changes: 4 additions & 0 deletions lib/fear/either.rb
Original file line number Diff line number Diff line change
Expand Up @@ -331,3 +331,7 @@ def Right(value)
end
end
end

require "fear/either_pattern_match"
require "fear/left"
require "fear/right"
2 changes: 2 additions & 0 deletions lib/fear/either_api.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require "fear/either"

module Fear
module EitherApi
# @param value [any]
Expand Down
15 changes: 7 additions & 8 deletions lib/fear/either_pattern_match.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require "fear/pattern_match"

module Fear
# Either pattern matcher
#
Expand All @@ -24,18 +26,12 @@ module Fear
# @note it has two optimized subclasses +Fear::LeftPatternMatch+ and +Fear::RightPatternMatch+
# @api private
class EitherPatternMatch < Fear::PatternMatch
LEFT_EXTRACTOR = :left_value.to_proc
public_constant :LEFT_EXTRACTOR

RIGHT_EXTRACTOR = :right_value.to_proc
public_constant :RIGHT_EXTRACTOR

# Match against +Fear::Right+
#
# @param conditions [<#==>]
# @return [Fear::EitherPatternMatch]
def right(*conditions, &effect)
branch = Fear.case(Fear::Right, &RIGHT_EXTRACTOR).and_then(Fear.case(*conditions, &effect))
branch = Fear.case(Fear::Right, &:right_value).and_then(Fear.case(*conditions, &effect))
or_else(branch)
end
alias success right
Expand All @@ -45,9 +41,12 @@ def right(*conditions, &effect)
# @param conditions [<#==>]
# @return [Fear::EitherPatternMatch]
def left(*conditions, &effect)
branch = Fear.case(Fear::Left, &LEFT_EXTRACTOR).and_then(Fear.case(*conditions, &effect))
branch = Fear.case(Fear::Left, &:left_value).and_then(Fear.case(*conditions, &effect))
or_else(branch)
end
alias failure left
end
end

require "fear/left_pattern_match"
require "fear/right_pattern_match"
9 changes: 0 additions & 9 deletions lib/fear/failure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,6 @@ class Failure
include RightBiased::Left
include FailurePatternMatch.mixin

EXTRACTOR = proc do |try|
if Fear::Failure === try
Fear.some([try.exception])
else
Fear.none
end
end
public_constant :EXTRACTOR

# @param [StandardError]
def initialize(exception)
@exception = exception
Expand Down
2 changes: 2 additions & 0 deletions lib/fear/failure_pattern_match.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ def success(*_conditions)
self
end
end

private_constant :FailurePatternMatch
end
2 changes: 2 additions & 0 deletions lib/fear/for_api.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require "fear/for"

module Fear
module ForApi
# Syntactic sugar for composition of multiple monadic operations. It supports two such
Expand Down
30 changes: 11 additions & 19 deletions lib/fear/future.rb
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
# frozen_string_literal: true

begin
require "concurrent"
rescue LoadError
puts "You must add 'concurrent-ruby' to your Gemfile in order to use Fear::Future"
end

module Fear
# Asynchronous computations that yield futures are created
# with the +Fear.future+ call:
Expand Down Expand Up @@ -249,7 +243,7 @@ def value
# )
#
def transform(success, failure)
promise = Promise.new(@options)
promise = Promise.new(**@options)
on_complete_match do |m|
m.success { |value| promise.success(success.(value)) }
m.failure { |error| promise.failure(failure.(error)) }
Expand All @@ -268,7 +262,7 @@ def transform(success, failure)
# future.map { |v| v * 2 } #=> the same as Fear.future { 2 * 2 }
#
def map(&block)
promise = Promise.new(@options)
promise = Promise.new(**@options)
on_complete do |try|
promise.complete!(try.map(&block))
end
Expand All @@ -294,7 +288,7 @@ def map(&block)
# end
#
def flat_map
promise = Promise.new(@options)
promise = Promise.new(**@options)
on_complete_match do |m|
m.case(Fear::Failure) { |failure| promise.complete!(failure) }
m.success do |value|
Expand Down Expand Up @@ -348,7 +342,7 @@ def select
#
#
def recover(&block)
promise = Promise.new(@options)
promise = Promise.new(**@options)
on_complete do |try|
promise.complete!(try.recover(&block))
end
Expand All @@ -374,7 +368,7 @@ def recover(&block)
# # but it performs two calls asynchronously
#
def zip(other)
promise = Promise.new(@options)
promise = Promise.new(**@options)
on_complete_match do |m|
m.success do |value|
other.on_complete do |other_try|
Expand Down Expand Up @@ -412,7 +406,7 @@ def zip(other)
# f.fallback_to(g) # evaluates to 5
#
def fallback_to(fallback)
promise = Promise.new(@options)
promise = Promise.new(**@options)
on_complete_match do |m|
m.success { |value| promise.complete!(value) }
m.failure do |error|
Expand Down Expand Up @@ -447,7 +441,7 @@ def fallback_to(fallback)
# end
#
def and_then
promise = Promise.new(@options)
promise = Promise.new(**@options)
on_complete do |try|
Fear.try do
Fear::Try.matcher { |m| yield(m) }.call_or_else(try, &:itself)
Expand Down Expand Up @@ -478,19 +472,17 @@ class << self
# @return [Fear::Future]
#
def failed(exception)
new(executor: Concurrent::ImmediateExecutor.new) do
raise exception
end
new { raise exception }
.yield_self { |future| Fear::Await.ready(future, 10) }
end

# Creates an already completed +Future+ with the specified result.
# @param result [Object]
# @return [Fear::Future]
#
def successful(result)
new(executor: Concurrent::ImmediateExecutor.new) do
result
end
new { result }
.yield_self { |future| Fear::Await.ready(future, 10) }
end
end
end
Expand Down
15 changes: 13 additions & 2 deletions lib/fear/future_api.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
# frozen_string_literal: true

begin
require "concurrent"
rescue LoadError
puts "You must add 'concurrent-ruby' to your Gemfile in order to use Fear::Future"
end

require "fear/awaitable"
require "fear/await"
require "fear/future"
require "fear/promise"

module Fear
# rubocop: disable Layout/LineLength
module FutureApi
Expand All @@ -13,8 +24,8 @@ module FutureApi
# f = Fear.future(executor: :io) { open('http://example.com') }
# f.map(&:read).each { |body| puts body }
#
def future(options = {}, &block)
Future.new(options, &block)
def future(**options, &block)
Future.new(nil, **options, &block)
end
end
# rubocop: enable Layout/LineLength
Expand Down
9 changes: 0 additions & 9 deletions lib/fear/left.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,6 @@ class Left
include RightBiased::Left
include LeftPatternMatch.mixin

EXTRACTOR = proc do |either|
if Fear::Left === either
Fear.some([either.left_value])
else
Fear.none
end
end
public_constant :EXTRACTOR

# @api private
def left_value
value
Expand Down
9 changes: 0 additions & 9 deletions lib/fear/none.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ class NoneClass
include RightBiased::Left
include NonePatternMatch.mixin

EXTRACTOR = proc do |option|
if Fear::None === option
Fear.some([])
else
Fear.none
end
end
public_constant :EXTRACTOR

# @raise [NoSuchElementError]
def get
raise NoSuchElementError
Expand Down
4 changes: 4 additions & 0 deletions lib/fear/option.rb
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,7 @@ def Some(value)
end
end
end

require "fear/option_pattern_match"
require "fear/some"
require "fear/none"
2 changes: 2 additions & 0 deletions lib/fear/option_api.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require "fear/option"

module Fear
module OptionApi
# An +Option+ factory which creates +Some+ if the argument is
Expand Down
10 changes: 6 additions & 4 deletions lib/fear/option_pattern_match.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require "fear/pattern_match"

module Fear
# Option pattern matcher
#
Expand All @@ -24,15 +26,12 @@ module Fear
# @note it has two optimized subclasses +Fear::SomePatternMatch+ and +Fear::NonePatternMatch+
# @api private
class OptionPatternMatch < Fear::PatternMatch
GET_METHOD = :get.to_proc
private_constant :GET_METHOD

# Match against Some
#
# @param conditions [<#==>]
# @return [Fear::OptionPatternMatch]
def some(*conditions, &effect)
branch = Fear.case(Fear::Some, &GET_METHOD).and_then(Fear.case(*conditions, &effect))
branch = Fear.case(Fear::Some, &:get).and_then(Fear.case(*conditions, &effect))
or_else(branch)
end

Expand All @@ -46,3 +45,6 @@ def none(&effect)
end
end
end

require "fear/some_pattern_match"
require "fear/none_pattern_match"
Loading

0 comments on commit cc40be5

Please sign in to comment.