Dry Monads Versions Save

Useful, common monads in idiomatic Ruby

v1.6.0

1 year ago

Changed

  • This version uses dry-core 1.0 (@flash-gordon)

Compare v1.5.0...v1.6.0

v1.5.0

1 year ago

Changed

  • Use zeitwerk for auto-loading dry-monads classes (@flash-gordon)
  • Task#then is deprecated in favor of Task#bind (@flash-gordon)
  • Minimal Ruby version is now 2.7 (@flash-gordon)
  • Either (old name of Result) was removed (@flash-gordon)

Compare v1.4.0...v1.5.0

v1.4.0

2 years ago

Added

  • Unit destructures to an empty array (flash-gordon)
  • When .value! called on a Failure value the error references to the value (rewritten + flash-gordon)
    begin
      Failure("oops").value!
    rescue => error
      error.receiver # => Failure("oops")
    end
    
  • Result#alt_map for mapping failure values (flash-gordon)
    Failure("oops").alt_map(&:upcase) # => Failure("OOPS")
    
  • Try#recover recovers from errors (flash-gordon)
    error = Try { Hash.new.fetch(:missing) }
    error.recover(KeyError) { 'default' } # => Try::Value("default")
    
  • Maybe#filter runs a predicate against the wrapped value. Returns None if the result is false (flash-gordon)
    Some(3).filter(&:odd?)  # => Some(3)
    Some(3).filter(&:even?) # => None
    # no block given
    Some(3 == 5).filter     # => None
    
  • RightBiased#| is an alias for #or (flash-gordon)
    None() | Some(6) | Some(7) # => Some(6)
    Failure() | Success("one") | Success("two") # => Success("one")
    

Fixed

  • Do notation preserves method visibility (anicholson + flash-gordon)

Changed

  • Coercing nil values to None with Some#fmap is officially deprecated. (flash-gordon) Switch to Some#maybe when you expect nil. This behavior will be dropped in 2.0 but you can opt-out of warnings for the time being
    Dry::Monads::Maybe.warn_on_implicit_nil_coercion false
    
  • Minimal Ruby version is 2.6

Compare v1.3.5...v1.4.0

v1.3.5

4 years ago

Added

  • Smarter keys deconstruction in pattern matching (flash-gordon)

Compare v1.3.4...v1.3.5

v1.3.4

4 years ago

v1.3.4 2019-12-28

Fixed

  • One more delegation warning happenning in do notation (flash-gordon)

Compare v1.3.3...v1.3.4

v1.3.3

4 years ago

v1.3.3 2019-12-11

Fixed

  • Incompatibility with Rails. Internal (!) halt exceptions now use mutable backtraces because spring mutates (!) them. For the record, this a bug in Rails (johnmaxwell)

Compare v1.3.2...v1.3.3

v1.3.2

4 years ago

v1.3.2 2019-11-30

Fixed

  • Warnings about keywords from Ruby 2.7 (flash-gordon)

Added

  • Pattern matching syntax was improved by implementing #deconstruct_keys. Now curly braces aren't necessary when the wrapped value is a Hash (flash-gordon)
    case result
    in Success(code: 200...300) then :ok
    end
    

Internal

  • Performance of do notation was improved for failing cases (1.2x to 1.3x on synthetic benchmarks) (flash-gordon)

Compare v1.3.1...v1.3.2

v1.3.1

4 years ago

v1.3.1 2019-09-07

Fixed

  • Added missing None#maybe :sweat_smile: (flash-gordon)

Compare v1.3.0...v1.3.1

v1.3.0

4 years ago

v1.3.0 2019-08-03

BREAKING CHANGES

  • Support for Ruby 2.3 was dropped.

Added

  • Result#either (waiting-for-dev)

    Success(1).either(-> x { x + 1 }, -> x { x + 2 }) # => 2
    Failure(1).either(-> x { x + 1 }, -> x { x + 2 }) # => 3
    
  • Maybe#to_result (SpyMachine + flash-gordon)

    Some(3).to_result(:no_value)   # => Success(3)
    None().to_result { :no_value } # => Failure(:no_value)
    None().to_result               # => Failure()
    
  • Do notation can be used with extend. This simplifies usage in class methods and in other "complicated" cases (gogiel + flash-gordon)

    class CreateUser
      extend Dry::Monads::Do::Mixin
      extend Dry::Monads[:result]
    
      def self.run(params)
        self.call do
          values = bind Validator.validate(params)
          user = bind UserRepository.create(values)
    
          Success(user)
        end
      end
    end
    

    Or you can bind values directly:

    ma = Dry::Monads.Success(1)
    mb = Dry::Monads.Success(2)
    
    Dry::Monads::Do.() do
      a = Dry::Monads::Do.bind(ma)
      b = Dry::Monads::Do.bind(mb)
    
      Dry::Monads.Success(a + b)
    end
    
  • {Some,Success,Failure}#[] shortcuts for building arrays wrapped within monadic value (flash-gordon)

    Success[1, 2] # => Success([1, 2]) 
    
  • List.unfold yields a block returning Maybe<Any>. If the block returns Some(a) a is appended to the output list. Returning None halts the unfloding (flash-gordon)

    List.unfold(0) do |x|
      if x > 5
        None()
      else
        Some[x + 1, 2**x]
      end
    end # => List[1, 2, 3, 4, 5]
    
  • Experimental support for pattern matching! :tada: (flash-gordon)

    case value
    in Failure(_) then :failure
    in Success(10) then :ten
    in Success(100..500 => code) then code
    in Success() then :empty
    in Success(:code, x) then x
    in Success[:status, x] then x
    in Success({ status: x }) then x
    in Success({ code: 200..300 => x }) then x
    end
    

    Read more about pattern matching in Ruby:

    Keep in mind this feature is experimental and can be changed by 2.7 release. But it rocks already!

Compare v1.2.0...v1.3.0

v1.2.0

5 years ago

v1.2.0 2019-01-12

BREAKING CHANGES

  • Support for Ruby 2.2 was dropped. Ruby 2.2 reached its EOL on March 31, 2018.

Added

  • Most of the constructors now have call alias so you can compose them with Procs nicely if you've switched to Ruby 2.6 (flash-gordon)

    pipe = -> x { x.upcase } >> Success
    pipe.('foo') # => Success('FOO')
    
  • List#collect gathers Some values from the list (flash-gordon)

    include Dry::Monads::List::Mixin
    include Dry::Monads::Maybe::Mixin
    # ...
    List[10, 5, 0].collect do |divisor|
      if divisor.zero?
        None()
      else
        Some(n / divisor)
      end
    end
    # => List[4, 2]
    

    Without block:

    List[Some(5), None(), Some(3)].collect.map { |x| x * 2 }
    # => [10, 6]
    
  • Right-biased monads got #flatten and #and (falsh-gordon)

    #flatten removes one level of monadic structure, it's useful when you're dealing with things like Maybe of Maybe of something:

    include Dry::Monads::Maybe::Mixin
    
    Some(Some(1)).flatten # => Some(1)
    Some(None()).flatten # => None
    None().flatten # => None
    

    In contrast to Array#flatten, dry-monads' version removes only 1 level of nesting, that is always acts as Array#flatten(1):

    Some(Some(Some(1))).flatten # => Some(Some(1))
    

    #and is handy for combining two monadic values and working with them at once:

    include Dry::Monads::Maybe::Mixin
    
    # using block
    Some(5).and(Some(3)) { |x, y| x + y } # => Some(8)
    # without block
    Some(5).and(Some(3)) # => Some([5, 3])
    # other cases
    Some(5).and(None()) # => None()
    None().and(Some(5)) # => None()
    
  • Concise imports with Dry::Monads.[]. You're no longer required to require all desired monads and include them one-by-one, the [] method handles it for you (flash-gordon)

    require 'dry/monads'
    
    class CreateUser
      include Dry::Monads[:result, :do]
    
      def initialize(repo, send_email)
        @repo = repo
        @send_email = send_email
      end
    
      def call(name)
        if @repo.user_exist?(name)
          Failure(:user_exists)
        else
          user = yield @repo.add_user(name)
          yield @send_email.(user)
          Success(user)
        end
      end
    end
    
  • Task.failed is a counterpart of Task.pure, accepts an exception and returns a failed task immediately (flash-gordon)

Compare v1.1.0...v1.2.0