monadic icon indicating copy to clipboard operation
monadic copied to clipboard

Cannot specify nil as the desired fetch value from Monadic:Nothing

Open paploo opened this issue 10 years ago • 3 comments

Example: I have a person object that might have a spouse object, and I want their first_name. So, if all is good, I'd call person.spouse.first_name. Of course, if we have no spouse there would be a crash of no method first_name on nil, so I use Maybe:

first_name = Maybe(person).spouse.first_name.fetch(nil)

In this case, as per idiomatic Ruby, I want nil as the default first_name if there is none. However, the value of first_name is not nil, but rather Monadic::Nothing, which is true valued in if satements! In other words

# Fails to work correctly because first name is Monadic::Nothing, not nil as expected!
do_something(first_name) if first_name

The bug is at https://github.com/pzol/monadic/blob/master/lib/monadic/maybe.rb#L78, where you use a magic value for the argument to decide if you have one, and that magic value happens to be a common default for idiomatic Ruby.

My recommended fix would be to capture the args as follows, which gives exactly the same behavior as the current version, but honors any default value:

def fetch(*args)
  case args.length
  when 0
    self
  when 1
    args.first
  else
    raise ArgumentError, "wrong number of arguments (#{args.length} for 0..1)", caller
  end
end

paploo avatar Mar 20 '14 17:03 paploo

On second thought, I'm not sure why you'd ever want fetch to return Nothing, since the point of fetch is to unwrap the value in the monad and return to Ruby land. Why not just implement as:

def fetch(default=nil)
  default
end

paploo avatar Mar 20 '14 17:03 paploo

@paploo have you ever found a sound way to get around this?

SkyWriter avatar Jan 22 '16 07:01 SkyWriter

Hi,

Made a PR for this. Basically if you don't give an explicit argument it will return Nothing as before. If you explicitly say "give me nil (or whatever value)" it will for Nothing.

Maybe(nil)._ == Nothing
Maybe(nil)._(nil) == nil
Maybe(nil)._('') == ''

EDIT: Just read @paploo's implementation, had the same idea :)

EDIT2: @paploo the only problem I found with making fetch return the default is the Elvis Operator.

# if:
nil._? == nil
# then we cant do:
nil._?.a.b # will throw undefined method `a' for nil:NilClass
# so the result should stay as it currently is
nil._?.a.b == Nothing

There may be other reasons, but I'm not aware of them

sdbondi avatar Jan 28 '16 11:01 sdbondi