Coder Social home page Coder Social logo

dry-rb / dry-monads Goto Github PK

View Code? Open in Web Editor NEW
727.0 727.0 135.0 1.14 MB

Useful, common monads in idiomatic Ruby

Home Page: https://dry-rb.org/gems/dry-monads

License: MIT License

Ruby 99.28% Shell 0.05% HTML 0.67%
dry-rb functional-programming gem monad ruby rubygem

dry-monads's People

Contributors

actions-user avatar amhol avatar anicholson avatar artofhuman avatar borisaka avatar citizen428 avatar cllns avatar damncabbage avatar dry-bot avatar dsounded avatar flash-gordon avatar hovsater avatar kml avatar maknz avatar maximilianofelice avatar mctaylorpants avatar nicolas-besnard avatar nkondratyev avatar olleolleolle avatar parndt avatar radanskoric avatar rewritten avatar saverio-kantox avatar skryukov avatar solnic avatar timriley avatar ttdonovan avatar tylerhunt avatar vladislav-yashin avatar waiting-for-dev avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dry-monads's Issues

Task#then alias for #bind conflicts with Kernel#then

Dry::Monads::Task has an alias for its bind method as then.

Ruby 2.6 itself added Kernel#then as an alias to Kernel#yield_self (first note) ~11 months after dry-monads added its alias.

dry-monads's then takes precedence, but conflicting with Kernel methods is something we generally avoid.

Since it's just a helpful alias, and bind is already used a lot in dry-monads, I think it could be OK to just remove this alias, though that's technically a breaking change (or could wait for a 2.0 release, since this is pretty low stakes.)

Destructuring of RightBiased value is inconsistent and ends up breaking Hash values and arguments

Using different forms of binding, the destructuring has different way of breaking.

Set up:

class Data < Hash
  # This is the data that's passed around. Has method access and different to_s for the sake of testing
  def method_missing(name, *args)
    return fetch(name) if key?(name) && args.empty?
    super
  end

  def to_s
    "<Data #{super}>"
  end
end

add_wheel = ->(car, wheel) { car.wheels << wheel; car }

Break 1: wrong number of arguments (1 for 2)
The proc receives a single plain hash {color: 'red', wheels: [], radius: 36}

Right(Data[color: 'red', wheels: []])
  .bind(Data[radius: 36], &add_wheel)

Break 2: The second argument is converted to plain hash:

Right(Data[color: 'red', wheels: []])
  .bind(add_wheel, Data[radius: 36])
# => <Data {:color=>"red", :wheels=>[{:radius=>36}]}>

It should have been <Data {:color=>"red", :wheels=>[<Data {:radius=>36}>]}>

Break 3 (related to break 2): undefined method 'radius' for {:radius=>36}:Hash

add_big_wheel = ->(car, wheel) { car.wheels << wheel if wheel.radius > 35; car }

Right(Data[color: 'red', wheels: []])
  .bind(add_big_wheel, Data[radius: 36])

(The same happens with Result instead of Right of course, the examples are with version 0.3.1)

Spring (Rails pre-loader) and dry-monads are incompatible

Thank you for your work on the dry project.

I wanted to make you aware of this incompatibility with spring. Even though I'm opening this ticket on your project first, I intend to open a ticket with spring as well.

Describe the bug

Spring (Rails pre-loader) and dry-monads are incompatible. Spring replaces Kernel#raise with a version that sanitizes backtrace. dry-monads raises an instance of Halt with a frozen EMPTY_ARRAY. An error is generated when Failure(..) is used an a dry-monads Do implementation.

Spring is a installed by default on rails new

To Reproduce

https://github.com/johnmaxwell/spring_dry_monads_break

require 'dry/monads/do'

class BreakHalt
  include Dry::Monads[:result]
  include Dry::Monads::Do.for(:call)

  def call()
    yield break_halt
  end

  def break_halt
    Failure('anything')
  end
end

BreakHalt.new.call

Results in an error like:

.../lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:305:in `reject!': can't modify frozen Array (FrozenError)

Expected behavior

Returns Failure instance

Your environment

  • Affects my production application: NO
  • Ruby version: 2.6.5
  • OS: Mac
  • Rails: 6.0.1
  • Spring: 2.1.0
  • dry-monads: 1.3.2

Should `or` return a instance of the given Monad?

I am wondering if it would be better if or would return Some or Right instead of the result of the given block. Are there any arguments against it, because right now the developer has to make sure, that the result includes conversion to the given type.
However I am aware that this assumes that the block result is always treated as success.

bind_err (or a way to execute code on None or Left)

Might seem stupid, but based on all Monads, there is no way for me to execute code on a None or a Left.

My usecase is for example in a service object returning a monad in a controller:

I want to

result.bind do |value|
  render json: value.as_json
end.bind_err do |err|
  render_error err
end

Unfortunately, can't do it without checking the type.
In addition a simple way to identify if an object is a monad will be extremely helpful: allows interacting with "legacy code" more easily, and wrap the object in a monad if it's not wrapped already.

Do notation with block doesn't yield methods

Describe the bug

After using dry-transaction I had a habit to call "commands" special way with passing a block to it and matching result, example:

TransactionCommand.new.call(params) do |result|
  result.success { process_success }
  result.failure { process_failure }
end

When I tried to do the same with transaction, i encountered bug: yield some_method always returns nil.
I know that passing a block to do monad isn't a case, but it'll be nice to raise some error if it's not supported.

To Reproduce

require 'dry/monads'
require 'dry/monads/do'

class ValidateAccount
  include Dry::Monads[:result]
  include Dry::Monads::Do.for(:call)

  def call(params)
    values = yield validate(params)
    Success(values)
  end

  def validate(params)
    Success(params)
  end
end

# always returns `nil`
ValidateAccount.new.call({ hello: 'world' }) do |result|
  result.success { |values| puts values }
  result.failure { |error| puts error }
end

# returns `Success({ hello: 'world' })`
ValidateAccount.new.call({ hello: 'world' }) do |result|
  result.success { |values| puts values }
end

Expected behavior
I'm not sure what behavior should be, but here are possible scenarios:

  1. Raise error if block passed to #call
  2. Support dry-transaction-like code: https://dry-rb.org/gems/dry-transaction/0.13/basic-usage/#calling-a-transaction

Your environment

  • Affects my production application: NO
  • Ruby version: 2.6.5
  • OS: macOS 10.15.5

Right.to_some crached with argument error

M.Right(nil).to_maybe
ArgumentError: nil cannot be some
from /Users/me/.rvm/gems/ruby-2.3.1/gems/dry-monads-0.2.0/lib/dry/monads/maybe.rb:47:in `initialize'

I think Dry::Monads::Maybe(value) instead Maybe::Some.new(value) is a solution

`include Dry::Monads[:do]` doesn't work on class methods

It appears the syntax include Dry::Monads[:do] doesn't work on class methods.

For example:

class MyTest
  class << self
    include Dry::Monads[:try, :result, :do]

    def foo(x)
      a = yield bar(x)
    end

    def bar(x)
      Try { 10 / x }.to_result
    end
  end
end
irb(main):066:0> MyTest.foo(0)
Traceback (most recent call last):
  2: from (irb):66
  1: from app/persistence/my_test.rb:45:in `foo'
LocalJumpError (no block given (yield))

The include Dry::Monads[:try, :result, :do] works as expected if not implemented as class methods.

Also, this syntax works fine on class methods:

class MyTest
  class << self
    include Dry::Monads[:try, :result]
    include Dry::Monads::Do.for(:foo)

    def foo(x)
      a = yield bar(x)
    end

    def bar(x)
      Try { 10 / x }.to_result
    end
  end
end
irb(main):059:0> MyTest.foo(0)
=> Failure(#<ZeroDivisionError: divided by 0>)

I looked through the issues briefly. Issue #92 addressed difficulty getting do-notation working with class methods, but only used the Do.for(:foo) syntax. Also #68 is a different issue, but I noted the syntax to include Dry::Monads::Do::All; I tried it and ran into the same LocalJumpError.

My environment

  • Affects my production application: NO
  • Ruby version: 2.7.1
  • OS: macOS 10.15.5
  • dry-monads: 1.3.5

can not use _url helper

Describe the bug

I have classes:

require 'dry/monads'
require 'dry/monads/do'

class BaseService
  include Dry::Monads[:result, :do]
end
class Create < BaseService
     include Rails.application.routes.url_helpers
     def call
        path = post_url
     end
end

When I call post_url then I have error:
post_url StackError: stack level too deep
/Users/mateuszbialowas/.asdf/installs/ruby/3.1.0/lib/ruby/gems/3.1.0/gems/dry-monads-1.5.0/lib/dry/monads/do.rb:128:in `default_url_options'

When I remove the inheritance from BaseService, then I can call post_url with no error.

How can I use dry monads with do notation and call URL helpers?

Expected behavior

I would like to call post_url with no error. I want to be returned full path: http://localhost:3000/posts

My environment

  • Affects my production application: NO
  • Ruby version: 3.1.0
  • OS: mac 13.3

List#map could return Enumerator

Currently List#map raises an error if no block is given. However this makes it impossible to do something like map.with_index as it is possible with all regular ruby Enumerables. I would really like to have this normal behaviour for List#map as well.
Could this be done and are there any cons to it, that I am not aware of?

Reflect method aliases in "constructors"?

Unlike #42 this not supposed to be a major change, but as noted there the Either monad does somewhat double as a Result monad. So given that right? and left? are already aliased to success? and failure?, would it be reasonable to also alias the "constructors" defined in the mixin?

Essentially just this:

index 083c4f0..f48b32e 100644
--- a/lib/dry/monads/either.rb
+++ b/lib/dry/monads/either.rb
@@ -210,2 +210,3 @@ module Dry
         end
+        alias Success Right

@@ -216,2 +217,3 @@ module Dry
         end
+        alias Failure Left
       end

For reference, Rust originally had both, but then went with Result for the standard library. Relevant quote:

[W]e actually used to have both Either and Result, until one day we went through and realized that no code in existence was using Either and decided to go all-in on Result instead.

On a side-note, I find it would make dry-transaction code much more readable to the average userโ„ข:

def validate(input)
  if input[:email].nil?
    Failure(:not_valid)
  else
    Success(input)
  end
end

Pattern Matching is broken for Array

Describe the bug

Pattern matching doesn't work for Success([1]) in Success([a]), hoverer it works for Success[1] in Success[a]

To Reproduce

[1] pry(main)> Success([1]) in Success([a])
NoMatchingPatternError: Success([1])
from (pry):41:in `<main>'

# The working examples:
[2] pry(main)> Success[1] in Success[a]
=> nil
[3] pry(main)> Success([1]) in Success[a]
=> nil

Expected behavior

Success([a]) should work exactly the same as Success[a], otherwise examples should be updated.

My environment

  • Affects my production application: No, since there is a workaround (eg Success[1])
  • Ruby version: ruby 2.7.2p137
  • OS: MacOS Big Sur

Do notation doesn't work for children classes

Hi, thank you for a great Gem!
I have such problem:

class BaseCommand
  include Dry::Monads::Result::Mixin
  include Dry::Monads::Do.for(:call)
  
  def validate
    Success(:ok)
  end
end

class CreateCommand < BaseCommand
  def call
    yield validate
  end
end

Expected behaviour:

:ok

Actual behaviour:

LocalJumpError: no block given (yield)

Looks like this is broken because current solution(prepending module that overrides selected methods and makes yield work without block) works only for parent class and children have their own class in the first place in the method lookup chain

Do you have making it works for children in your plans?
Thanks!

P.S. If I understand the problem correctly then this solution might work
https://stackoverflow.com/questions/49897647/ruby-prepend-module-how-to-prepend-module-to-the-beginning-of-ancestors-array
I can try it a bit later

`include Dry::Monads[:do]` removes private method protection on instance methods

Describe the bug

Adding include Dry::Monads[:do] to a class silently removes private (or indeed any) visibility modifiers from all methods.

To Reproduce

  1. run ruby minimal_case.rb from this gist (sorry, GH wouldn't let me attach the file directly)

Expected behaviour

Expected: raises NoMethodError (private method 'reveal' called for #<WithSecret:0xdeadbeef>
Actual: outputs Oops, I swore I wouldn't tell! to STDOUT

Your environment

  • Affects my production application: NO
  • Ruby version: 2.6.6, 2.5.1
  • OS: Linux

Notes

  • We notice that wrap_method doesn't accept a protection modifier, so all redefined methods become public by default.
  • Do::All uses Module::instance_methods to determine what to wrap - but (since at least Ruby 2.0) Module also provides public_instance_methods, protected_instance_methods, and private_instance_methods. Could these be used to give the wrapper methods the same visibility as the methods they wrap?
  • Given how private/protected/public operate, it might be tricky to do this without accidentally setting the default visibility within the class to a surprising value. I haven't tested this, though.

Pattern matching in class method requires `include` instead of `extend`

Describe the bug

When pattern matching inside a class method an include Dry::Monads[:maybe] is needed, the extend Dry::Monads[:maybe] is not enough and causes a crash.

To Reproduce

Simple script:

class Foo
  include Dry::Monads[:maybe]
  extend Dry::Monads[:maybe]

  def self.foo
    case Some(4)
    in Some(a)
      puts a
    in None()
      puts 'none'
    end
  end
end

Removing any include or extend will cause this script to fail:

  1. Removing extend causes Some(4) to raise NoMethodError: undefined method Some' for Foo:Class` (makes sense)
  2. Removing extend causes Some(a) to raise NameError: uninitialized constant Foo::Some (?)

Expected behavior

Ideally I'd like to only have to extend Dry::Monads[:maybe] for a class method, not both.

My environment

  • Affects my production application: NO
  • Ruby version: 3.1.4p223
  • OS: Docker (Alpine)

monads transforms dry-structs into hash

After update to dry-monads 0.3.0 monads convert dry-struct to hashes:

class A < Dry::Struct
  attribute :name, Types::Strict::String
end

m = M.Maybe(A[name: '123'])
# => Some(#<PersistCustomerCommand::A name="123">)
m.bind { |a| a.inspect }
=> "{:name=>\"123\"}"

undefined Right after upgrading to 1.0.0

Hi.
After upgrading Dry-Transaction and Dry-Monads we face a big issue with breaking changes.

Currently in each transaction we fail with:
NoMethodError: undefined method Right for.....

As I understand this happen due to replacing Right/Left with Success/Failure, but this change was stated as backward compatible.

However the worst part is that we use method 'value' on Right/Left instance to retrieve transaction results. But currently Success class no longer has method value which was replaced with value!

Can you help to understand do you plan to support backward compatibility and help to rectify such cases and share what are correct usage of DryTransaction to avoid such cases in future?

Honestly these breaking changes force to rework all parts where DryMonads were ever used and cancel almost all benefits of continue using Dry as risk of having similar troubles in future

Our Success(Right) context is:
dry-monads (0.4.0)
dry-core (~> 0.3, >= 0.3.3)
dry-equalizer
dry-transaction (0.13.0)
dry-container (>= 0.2.8)
dry-events (>= 0.1.0)
dry-matcher (>= 0.7.0)
dry-monads (>= 0.4.0)

Our Failure(Left) context is:
dry-monads (1.0.0)
dry-core (~> 0.4, >= 0.4.4)
dry-equalizer
dry-transaction (0.13.0)
dry-container (>= 0.2.8)
dry-events (>= 0.1.0)
dry-matcher (>= 0.7.0)
dry-monads (>= 0.4.0)

Map monoid

Hi there, I was curious whether you'd be interested in implementing the Map monoid in Dry-Monad, an inspiration for this is https://github.com/DrBoolean/immutable-ext . I was thinking in the line of

name_map = Map(
 {
   first: Some("Joe"), 
   last: None(),
}
)

# Hash -> String
SayHi = -> (name_hash) {
  name_hash[:first] + name_hash[:last] + ', hi' }

name.bind{|name_hash| Dry::Monads.Maybe(SayHi(name_hash) } # => name_map, because there's a None(); had last name existed, it would be Some("Joe Smith, hi")

Perhaps you could use a hand in implementing this?

[Feature request] List.unfold

I recently added the following to encapsulate pagination and I think it could be generally useful. Please consider implementing something similar.

require 'dry/monads/list'
require 'dry/monads/maybe'
require 'dry/monads/result'

module Dry
  module Monads
    class List
      def self.unfold(cursor)
        list = []

        loop do
          result = yield cursor

          case result
          when Monads::Success
            maybe = result.value!

            case maybe
            when Monads::Some
              cursor = maybe.value!.first
              list << maybe.value!.last
            when Monads::None
              break
            end
          when Monads::Failure
            return result
          end
        end

        Monads::Success(new(list))
      end
    end
  end
end

Traverse of a list with an unfrozen empty array returns a list with a frozen empty array

Describe the bug

While traversing a Dry::Monads::List with an empty unfrozen array, returns a Dry::Monads::List but with a frozen empty array inside. It's an inconsistent behavior as for a Dry::Monads::List with an array with some content, it always returns a Dry::Monads::List with unfrozen content.

To Reproduce

Array with content example

Dry::Monads::List.new([Success(1), Success(2)]).typed(Dry::Monads::Result).traverse.fmap(&:value)
=> Success([1, 2])
Dry::Monads::List.new([Success(1), Success(2)]).typed(Dry::Monads::Result).traverse.fmap(&:value).value!.frozen?
=> false

Empty array example

Dry::Monads::List.new([]).typed(Dry::Monads::Result).traverse.fmap(&:value)
=> Success([])
Dry::Monads::List.new([]).typed(Dry::Monads::Result).traverse.fmap(&:value).value!.frozen?
=> true

Expected behavior

While passing a Dry::Monads::List with an empty unfrozen array, should return a Dry::Monads::List with an empty unfrozen array as well

My environment

  • Affects my production application: YES
  • Ruby version: 3.2.2

Proposed solution

The solution seems pretty simple, it requires to change the dry/monads/list.rb#381 from

EMPTY = List.new([].freeze).freeze

to

EMPTY = List.new([]).freeze

Contrary to the doc comment, List#map might not return an array.

Describe the bug

https://github.com/dry-rb/dry-monads/blob/master/lib/dry/monads/list.rb#L139 states:

Note that this method returns an Array instance, not a List

However, this isn't true; when called with a block, List#map will return a List.
It also conflicts with the @return [List,Enumerator] annotation.

I expected the doc comment would be accurate and consistent.
Either the comment needs updating, or the method is working incorrectly.

To Reproduce

require 'dry/monads'
require 'dry/monads/list'

list = Dry::Monads::List['a', 'b', 'c']
mapped = list.map { |char| char.upcase }

pp mapped

Output:

$ ruby list-test.rb 
List["A", "B", "C"]

Expected behavior

I expected the output to be an array, like ["A", "B", "C"]

Your environment

  • Affects my production application: NO
  • Ruby version: ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-linux]
  • OS: Linux, Ubuntu 14.04.6 LTS

Fixed-Result resolution fails if first referenced by constant.

Describe the bug

The Result monad can only be included in its Fixed form (e.g. Dry::Monads::Result(Error)) if it has previously been referenced in its 'normal form' (e.g. Dry::Monads[:result]).

To Reproduce

Attempt to include a Fixed-Result into a class, exactly as seen in the Result documentation, section 'Adding Constraints to Failure values.'

This causes the class to fail construction, with the following error:

NameError (uninitialized constant Dry::Monads::Result::Fixed)

Expected behavior

The Result monad is included into the class successfully.

Known workaround

If the Result monad has previously been referenced in its normal form, the fixed form begins working as well. To work around this issue, I require the following file instead of dry/monads/result where it is needed:

require 'dry/monads/result'

Dry::Monads[:result]

This causes all necessary constants to be instantiated for future use.

Your environment

  • Affects my production application: NO (worked around)
  • Ruby version: 2.6.3
  • OS: Ubuntu 18.04 x86_64

Pattern matching `Success` containing an array returns a surprising result

Describe the bug

When you pattern match a Result that happens to contain an array in a case statement, the destructured contents of the Result contains the contents of the array, not the array.

We discovered this because we have a function that decodes JSON and returns the contents in a Result (Success if it parsed correctly, Failure if it didn't). We don't know ahead of time whether or not the Result will contain an array or not. It's not really a problem to use an if statement in this situation (result.value! contains the correct value), but it's very surprising that you have to.

To Reproduce

result = Success([42])

case result
in Success(value)
  value
in Failure(err)
  err
end

Returns 42

If instead you set: result = Success([1, 2, 3, 4])

The above case statement will generate a NoMatchingPatternError.

Expected behavior

The case statement should return [42] (or in the second instance [1, 2, 3, 4])

My environment

  • Affects my production application: YES/NO
  • Ruby version: 3.0.1
  • OS: N/A

Implementing my own monad?

Hey there, thanks for the great work!

Although I've noticed you've been working on dry-effects for algebraic effects (and it might probably be the best solution for my problem), I was wondering what path should I take if I wanted to implement my own monads (logging monad and retry monad would be my top priority for my use case right now).

And btw, is there any roadmap on dry-effects?

setup new gem services

Now that the proposal branch has been merged into master. Could someone with the correct credentials please setup services for Travis CI and CodeClimate. Thanks!

Maybe for or not triggered when proc added

What is going on:

This works:

require 'dry-monads'

M = Dry::Monads

add_two = -> (x) { M.Maybe(x + 2) }
not_add = -> { M.Some(0) }

# This works fine
M.Maybe(nil).or { M.Some(0) }.bind(add_two) # => Some(0)

This does not

require 'dry-monads'

M = Dry::Monads

add_two = -> (x) { M.Maybe(x + 2) }
not_add = -> { M.Some(0) }

M.Maybe(nil).or(not_add).bind(add_two)

resulting in:

Traceback (most recent call last):
monad.rb:8:in `<main>': undefined method `bind' for #<Proc:[email protected]:6 (lambda)> (NoMethodError)
Did you mean?  binding

`Some` raises exception if included using `dry/monads/maybe`

Whenever I launch irb and require dry/monads/maybe, I cannot call a constructor due to exception

irb(main):001:0> require 'dry/monads/maybe'
=> true
irb(main):002:0> Dry::Monads.Some(2)
Traceback (most recent call last):
       16: from /Users/moroz/.rvm/gems/ruby-2.5.3@global/gems/bundler-1.17.2/exe/bundle:22:in `<top (required)>'
       15: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/friendly_errors.rb:124:in `with_friendly_errors'
       14: from /Users/moroz/.rvm/gems/ruby-2.5.3@global/gems/bundler-1.17.2/exe/bundle:30:in `block in <top (required)>'
       13: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/cli.rb:18:in `start'
       12: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/vendor/thor/lib/thor/base.rb:466:in `start'
       11: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/cli.rb:27:in `dispatch'
       10: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/vendor/thor/lib/thor.rb:387:in `dispatch'
        9: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'
        8: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
        7: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/cli.rb:463:in `exec'
        6: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/cli/exec.rb:28:in `run'
        5: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/cli/exec.rb:74:in `kernel_load'
        4: from /Users/moroz/.rvm/rubies/ruby-2.5.3/lib/ruby/gems/2.5.0/gems/bundler-1.17.2/lib/bundler/cli/exec.rb:74:in `load'
        3: from /Users/moroz/.rvm/rubies/ruby-2.5.3/bin/irb:11:in `<top (required)>'
        2: from (irb):2
        1: from /Users/moroz/.rvm/gems/ruby-2.5.3@qlean-app/gems/dry-monads-1.0.1/lib/dry/monads/maybe.rb:212:in `Some'
NameError (uninitialized constant Dry::Monads::Maybe::Mixin::Constructors::Undefined)

Dry::Monads.None works like a charm.

If nobody fixes it by weekend, I'm going to send a PR

Add gem page for dry-monads to dry-rb.org

I've submitted a pull request to dry-rb.org, but also want to leave a note in this repo that "Usage" documentation has been moved. Once the pull request has been accepted and published. The README.md of this repo can updated with link to the documentation.

dry-rb/dry-rb.org#62

Suggestion: `List#traverse` should be `List#sequence`

Coming from Haskell and Scala, the naming of List#traverse threw me off.

The Haskell signatures of both functions for reference:
sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
traverse :: (Traverseable t, Applicative f) => (a -> f b) -> t a -> f (t b)

Support `#<=>` over Maybe

Currently, <=> over Maybe values returns 0 if both sides are None, and nil otherwise. We could lift <=> over Maybe.

Examples

None <=> Some(2)
=> -1

Some(3) <=> None
=> 1

None <=> None
=> 0

# Calls <=> on the contents of each Some
Some(3) <=> Some(4)
=> -1

Can't extract wrapped array through pattern-matching

Describe the bug

When a right-biased type is wrapping an array value, it's not possible to unwrap it via pattern-matching.

To Reproduce

require "dry/monads"
include Dry::Monads[:result]
Success(1) in Success(x) # => true
x # => 1 AS EXPECTED
Success([1]) in Success(x) # => true
x # => 1 IT SHOULD BE [1]
Success([1, 2]) in Success(x) # => true
x # => 1 IT SHOULD BE [1, 2]

Expected behavior

The wrapped value should be extracted as it is.

My environment

  • Affects my production application: NO
  • Ruby version: v3.2

Proposed solution

We should stop branching depending on whether the wrapped value is an array. Instead, #deconstruct should be simply:

def deconstruct
  if Unit.equal?(@value)
    EMPTY_ARRAY
  else
    [@value]
  end
end

However, the above would be a breaking change. Now we have:

Success([1]) in [1] # => true

The above would change:

Success([1]) in [1] # => false

IMO, we should nonetheless change it, as the most common use-case for results is extracting the wrapped value after some operations have been performed.

I'm happy to submit a PR fixing the issue. If we consider it as a bug fix, it should go into the next minor or patch release. However, if we consider it as a new feature breaking code, we should wait until v2.0.

v1.1.0 NameError: uninitialized constant Dry::Monads::List

Hi there I have rails 5.1.2 with dry-monads 1.1.0 in Gemlock, an upgrade from 0.4.0. I am not getting errors such as

NameError: uninitialized constant Dry::Monads::List

and when calling Dry::Monads.Right(1) it throws:

NoMethodError: private method Right' called for Dry::Monads:Module`

What might I be missing?

Why there is no .ap method?

Hello everybody. Was playing with dry-monads recently and was very surprised by the fact that there is no .ap method, i know i can implement it using .bind but still it is very convenient method, why it is not in the library by default?

Consider an explicit Result monad

The Either monads doubles as Result in dry-rb. However I found the way .value works to be unexpected.

Consider this:

result = format_time("Invalid input")

"The time is #{result.value}"

This snippet would produce a wrong output e.g. "The time is Wrong Input".

Optimally I would do .success? before doing this. But ruby is not haskell and pattern matching is not required. So is easy to be lazy and code the happy path.

So maybe consider adding a Result monad that:

  • Raise if accessing value on failed result

result.value will raise error

  • Explicitly access the error using .error

e.g. result.error == "Wrong Input"

With something like this my above snippet will raise an error instead of giving me a wrong output, which I think is preferable.

Do-notation with class methods

Is there any way to use the do notation when the method is a class method rather than an instance method ?

We are struggling to find an appropriate syntax, and are reluctant to change our existing codebase which does not always instanciate classes ...

Lazy.wait results in error

Describe the bug

When using Lazy, and calling wait, I get an error about there being no live threads left.

To Reproduce

require 'dry/monads/all'
x = Dry::Monads.Lazy { 1 / 1 }.wait
fatal (No live threads left. Deadlock?)

Expected behavior

Lazy(1)

My environment

  • Affects my production application: NO
  • Ruby version: 2.7.0
  • OS: Ubuntu 20.10

Do-notation: wrapping method with global rescue works incorrect

Do-notation in method that has a global rescue works incorrectly. Here is an example.

class Documents::CreateDocument
  include Dry::Monads::Result::Mixin
  include Dry::Monads::Do.for(:call)

  def call(document, params)
    params = yield validate(params)
    document = yield persist(document, params)
    document = yield GlobalContainer['document.services.rename_document_command'].call(document)
    document = yield GlobalContainer['document.services.create_affiliations'].call(document)

    Success(document)
  rescue => e
    GlobalContainer['support.services.exception_notifier'].call(e)
    Failure(e)
  end

  # ...
end

Lets suggest that validate is a method that returns Dry::Monads::Failure for incorrect input.

[1] pry(main)> incorrect_params = {category_id: 0}
=> {:category_id=>0}
[2] pry(main)> result = Documents::CreateDocument.new.call(Document.new, incorrect_params)
=> Failure(#<Dry::Monads::Do::Halt: Dry::Monads::Do::Halt>)

I expected to receive Failure with result of #validate method, instead of it I received Failure with Dry::Monads::Do::Halt. This happens because https://github.com/dry-rb/dry-monads/blob/master/lib/dry/monads/do.rb#L137 is not called when Halt is raised here https://github.com/dry-rb/dry-monads/blob/master/lib/dry/monads/do.rb#L132. Halt is captured by global rescue is original method.

From my point of view this can be fixed by aliasing and calling original method instead of overriding it.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.