Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Many seems to be broken #8

Open
Bastes opened this issue Oct 10, 2015 · 4 comments
Open

Many seems to be broken #8

Bastes opened this issue Oct 10, 2015 · 4 comments

Comments

@Bastes
Copy link

Bastes commented Oct 10, 2015

First of all, thanks for your talk about Ruby monads, it blew my mind ^^

I've been trying to imitate your logic as a TDD exercise, and was comparing what I was writing to your code and found a difference puzzling me in the "Many" monad.

When I'm trying to use yours, I find that:

Blaher = Struct.new(:blahs)
Bloher = Struct.new(:blohs)
blahers = [Blaher.new([Bloher.new([1, 2, 3, 4])])]
Monads::Many.new(blahers).blahs.blohs.values

Ends up in a:

NoMethodError: undefined method `bloh' for [#<struct bloh=[1, 2, 3, 4]>]:Array

I thought it had something to do with the Many.from_value method and tried a quick-and-dirty fix with:

def from_value(value)
  Many.new(value)
end

It makes the method_missing do its job and return the subelements expected, though I find one test (spec/monads/many_spec.rb:49 the one that verifies that Many.from_value wraps the value in an array before wrapping it in the monad) fails.

I'm a bit unsure on what to do to fix this, so I thought I'd let you know ^^°

Again, thanks for the awesome talk and ideas.

@Bastes
Copy link
Author

Bastes commented Oct 10, 2015

I also tried another approach, changing the Monad.within method:

def within(&block)
  and_then do |value|
    self.class.new(block.call(value))
  end
end

Which, in turn, breaks the eventually specs ^^°:

spec/monads/eventually_spec.rb:73
spec/monads/eventually_spec.rb:83
spec/monads/eventually_spec.rb:102
spec/monads/eventually_spec.rb:107

Because it tries to initialize the monad giving it a block, not a proc as a parameter. I'm beginning to think part of the factoring went wrong somewhere, but I'm not yet sure what and where. I'll be trying to find that when I'm rested.

@sclinede
Copy link
Contributor

Hi. Can you describe what exactly you are expecting from your code? Maybe you have more meaningful example. I don't understand what do you think is broken...

sclinede added a commit to sclinede/monads that referenced this issue Feb 20, 2016
Use more delicate way (from ActiveSupport) to wrap an array instead of
just putting value into array anyways.
sclinede added a commit to sclinede/monads that referenced this issue Feb 20, 2016
@Bastes
Copy link
Author

Bastes commented Feb 22, 2016

Hi @sclinede, it's been a while so I may be fuzzy on the details, but I submitted a pull request to fix the thing I think was broken.

What was broken?

# an author writes books...
Author = Struct.new(:books)
# ...a book contains chapters...
Book = Struct.new(:chapters)

# ...here are some books, with or without chapters...
books = [
  Book.new([1, 2, 3, 4]),
  Book.new([5, 6, 7]),
  Book.new([]),
  Book.new([8, 9])
]

# ...here are authors with or without books...
authors = [
  Author.new(books),
  Author.new([])
]

# ...I'm trying to use the Many monad to extract the chapters from all the books from all the authors
Monads::Many.new(authors).books.chapters.values

# here is what I expected to obtain:
=> [1, 2, 3, 4, 5, 6, 7, 8, 9]

# but what I actually got is the following error:
# NoMethodError: undefined method `chapters' for #<Array:0x0055f546fb4e88>
# from lib/monads/monad.rb:11:in `public_send'
# from lib/monads/monad.rb:11:in `block in method_missing'
# from lib/monads/monad.rb:5:in `call'
# from lib/monads/monad.rb:5:in `block in within'
# from lib/monads/monad.rb:21:in `call'
# from lib/monads/monad.rb:21:in `block in ensure_monadic_result'
# from lib/monads/many.rb:10:in `map'
# from lib/monads/many.rb:10:in `and_then'
# from lib/monads/monad.rb:4:in `within'
# from lib/monads/monad.rb:10:in `method_missing'

Now here's why I think it went wrong, and how I fixed it:

def and_then(&block)
  block = ensure_monadic_result(&block)

  # this line looks wrong, because flat_map doesn't do what was expected of it
  Many.new(values.map(&block).flat_map(&:values))
end

# when I do this:
authors.map { |a| Monads::Many.from_value(a.books) }.flat_map(&:values)
# I get:
=> [[#<struct Book chapters=[1, 2, 3, 4]>, #<struct Book chapters=[5, 6, 7]>, #<struct Book chapters=[]>, #<struct Book chapters=[8, 9]>], []]

# But, wait, I hear you ask, this is not flat, not at all!!!
# No, it isn't. flat_map didn't do (map.flatten), it did (flatten.map)!

# Well then, instead of relying on flat_map, like this:
Many.new(values.map(&block).flat_map(&:values))
# We should do a map, then flatten it, likewise:
Many.new(values.map(&block).map(&:values).flatten)

@sclinede
Copy link
Contributor

OK, now I see. I think map + flatten is right solution. @tomstuart, what about you?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants