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

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

Closed
isikyus opened this issue Feb 4, 2020 · 6 comments
Closed

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

isikyus opened this issue Feb 4, 2020 · 6 comments
Labels

Comments

@isikyus
Copy link

isikyus commented Feb 4, 2020

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
@isikyus isikyus added the bug label Feb 4, 2020
@flash-gordon
Copy link
Member

Nice catch. It's puzzling, I'll take a deeper look later to find out why it states so.

@niksosf
Copy link

niksosf commented Sep 28, 2020

Here's a bit puzzling in line 145

in the else part of the statement, the array value is called Array#map on with the block but the if part is testing whether block exists.

def map(&block)
        if block
          fmap(block)
        else
          value.map(&block)
        end
      end

Is List#map, where the output is just ruby Array, intended to be distinct from fmap where the semigroup type is preserved

@flash-gordon
Copy link
Member

haven't looked into code yet but I'm pretty sure, yes, it's intentional. Think about it as two different methods

@niksosf
Copy link

niksosf commented Jan 7, 2021

Here's the part that I don't quite get. If there's no block given, then it code runs to the else part which in turn calls the block which is non-existent. Maybe it was meant to be something else in the else? The signature in the code for the return value [List, Enumerator] is correct though, so there must have been deliberation over this.

@isikyus
Copy link
Author

isikyus commented Jan 8, 2021

If there's no block given, then it code runs to the else part which in turn calls the block which is non-existent.

Looking at it again now, I think that's correct. When block is missing, value.map(&:block) is equivalent to value.map(&nil) — i.e. it returns an enumerator:

2.3.0 :003 > [].map(&nil)
 => #<Enumerator: []:map> 

This matches the return type annotation and test in the commit that added this (464b7ad).
In light of this, should the second line of the doc comment say something like this:

If called without a block, this method returns an Enumerator instance, not a List

@solnic
Copy link
Member

solnic commented Jan 13, 2021

@niksosf thanks for tracking this down, I believe you're correct, especially that this is what @flash-gordon wrote in the changelog

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

No branches or pull requests

4 participants