Skip to content

Add flatMap operator to Options#6404

Merged
dom96 merged 5 commits intonim-lang:develfrom
subsetpark:options-bind
Sep 27, 2017
Merged

Add flatMap operator to Options#6404
dom96 merged 5 commits intonim-lang:develfrom
subsetpark:options-bind

Conversation

@subsetpark
Copy link
Copy Markdown
Contributor

Adds the monadic bind operator to the options module.

@zielmicha
Copy link
Copy Markdown
Contributor

I would personally prefer a name, like then. I know >>= is used in Haskell, but I think then is more widespread.

@subsetpark
Copy link
Copy Markdown
Contributor Author

@zielmicha I associate then exclusively with promise handling in Javascript. I did a quick google for "option type then" but didn't see any examples. Where is that used as the name for the monadic bind?

@bluenote10
Copy link
Copy Markdown
Contributor

I know it from Scala as flatMap, which is often seen as a generalization of a monadic bind (e.g. illustrated here). I think taking a function A -> Option[A] is a bit limiting, there are good use cases for having A -> Option[B].

I would probably prefer flatMap over >>= because Nim's implementation is already closer to Scala's Option compared to Haskell's Maybe.

@subsetpark
Copy link
Copy Markdown
Contributor Author

@bluenote10 : 👍 on the Option[B] part. I'll leave the nomenclature as it is for now until more opinions come in.

@zielmicha
Copy link
Copy Markdown
Contributor

@subsetpark You are right that then is seldom used except for promises/futures.

I would now vote for flatMap. Does it make sense to also add flatMap/bind to other monadic structures in stdlib (futures, seq, anything else?)?

@subsetpark
Copy link
Copy Markdown
Contributor Author

@dom96 @Araq let me know if you have any opinions about nomenclature here.

@subsetpark
Copy link
Copy Markdown
Contributor Author

subsetpark commented Sep 19, 2017

@bluenote10 my issue with flatMap is that it relies on a usage of flatten that is not found anywhere in Nim.

@dom96
Copy link
Copy Markdown
Contributor

dom96 commented Sep 19, 2017

I'm fine with flatMap and to be honest I would prefer it to the >>= operator.


proc `$`*[T]( self: Option[T] ): string =
proc `$`*[T](self: Option[T]): string =
## Returns the contents of this option or `otherwise` if the option is none.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment on this proc seems a little misleading, might as well update it whilst you've got a PR touching the code anyway I reckon.

@Araq
Copy link
Copy Markdown
Member

Araq commented Sep 20, 2017

Nim is not Haskell, no '>>=' please. That could also be "shift right assign".

@subsetpark
Copy link
Copy Markdown
Contributor Author

@Araq do you like flatMap as well?

@Araq
Copy link
Copy Markdown
Member

Araq commented Sep 21, 2017

'flatMap' too means nothing. Can we come up with a name that says something?

@vegansk
Copy link
Copy Markdown
Contributor

vegansk commented Sep 21, 2017

@Araq, I guess that non-fp guys use nillable types, and fp guys know what is >>= and flatMap

We can't call it bind (another name of >>= operator)

There is another, but less well known name for this operation - chain - https://github.com/fantasyland/fantasy-land#monad

@bluenote10
Copy link
Copy Markdown
Contributor

Actually, I always found the name flatMap pretty meaningful and easy to teach. In the generalized sense: You map each element of a collection to a new subcollection, and these subcollections will be fattened into one final collection. This perspective also helps to see why flatMap is a generalization of other higher order functions like map (where each element is flat-mapped to a subcollection of size exactly 1) or filter (where each element is flat-mapped to a subcollection of either size 0 or 1). I'm not sure if this is a good place to come up with something new: Rust, Swift, and Java8 all have the flatMap concept (although I remember that Rust is using it inconsistently, iirc, on the options type they call it and_then).

@Araq
Copy link
Copy Markdown
Member

Araq commented Sep 21, 2017

It could be mapToOption, mapOption or just an overloaded map.

@vegansk
Copy link
Copy Markdown
Contributor

vegansk commented Sep 21, 2017

I guess that the better decision is to separate standard library from FP library. The Option type shouldn't be present in stdlib, it's name tells people that it should provide operations from monad, monoid, applicative typeclasses, etc. But it doesn't do it.

@zielmicha
Copy link
Copy Markdown
Contributor

'flatMap' too means nothing. Can we come up with a name that says something?

I agree with @bluenote10 and I would say that is hard to make a name that has more meaning. It is literally map + flatten.

map is already defined for Option. flatten is not, but it's a good name for proc(Option[Option[A]]): Option[A], because it flattens the type.

@subsetpark
Copy link
Copy Markdown
Contributor Author

I'm of the opinion that flatMap is pretty meaningless, outside of a well-established monadic/whatever Scala monads are context. In my view the functional meaning of this operator is that it maintains the monadic (in this case, Optional) context - instead of map, which "kicks you back out", it "keeps you in". So for me the meaning is something like continueWith or integrate.

But anyhow - much more strongly than believing against flatMap, I do believe it belongs in the stdlib. I never knowingly reach for the Monad abstraction, but I constantly reach for the Option type. And I'm not alone in this respect. I think it's totally valid for there to be a separate FP library somewhere, with a full implementation of functors, applicatives, and monads, and if it wants it can re-implement the Maybe monad on top of its abstraction. But this particular module, with this particular type, is invaluable for everyday programming, regardless of the abstraction it exemplifies; much more so than nillable types, that's for sure!

To that same point I would hesitate to make flatMap make sense by inserting a flatten procedure as well. That's the sort of thing that does make sense for a generic abstraction but hardly seems pertinent to an option type.

How do we resolve this?

@subsetpark subsetpark changed the title Add >>= operator to Options Add flatMap operator to Options Sep 25, 2017
@subsetpark
Copy link
Copy Markdown
Contributor Author

After some discussion, @dom96 @Araq are aligned: we are going with flatMap, as well as adding flatten to make the usage clear. This PR is ready to merge.

Copy link
Copy Markdown
Contributor

@dom96 dom96 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit but otherwise good to go.

else:
result = none(int)

check( some(1).flatMap(addOneIfNotZero) == some(2) )
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This spacing inside the parenthesis should be removed.

@vegansk
Copy link
Copy Markdown
Contributor

vegansk commented Sep 26, 2017

@andreaferretti
Copy link
Copy Markdown
Collaborator

@vegansk Seems compatible with what is written here, isn't it?

@vegansk
Copy link
Copy Markdown
Contributor

vegansk commented Sep 26, 2017

@andreaferretti , @subsetpark sorry, I read it wrong :-(

@subsetpark
Copy link
Copy Markdown
Contributor Author

@andreaferretti @vegansk now you guys have me really confused... the last line in @vegansk 's example is not compatible with the current implementation, which only goes Option[Option[A]] -> Option[A] (as we discussed on IRC, @andreaferretti ). The last line is going from Option[A] -> Option[A], that is, presumably performing a no-op on a value if it's already only a single level of Option. I can modify flatten to accept Option[A] and perform a no-op. I don't know Scala very well, is there a tradeoff to extending the function like this?

@subsetpark
Copy link
Copy Markdown
Contributor Author

subsetpark commented Sep 26, 2017

What further muddies the waters is that https://stackoverflow.com/questions/22511483/understanding-option-flatten-in-scala#22511696 seems to indicate that flatten(none(int)) == none(int), but flatten(some(12)) is actually a compiler error. Which seems like a behavior that might make a lot more sense for Scala than for Nim. (Also, maybe is not possible, since Option here is not an ADT, and has/not has is only available at runtime)

@andreaferretti
Copy link
Copy Markdown
Collaborator

The thing is, None is polymorphic. flatten is definitely defined on Option[Option[A]], and None is a member of it (well, None[Option[Option[A]] is). So it makes sense to call flatten on None (as a member of Option[Option[A]]).

Now, you may object that the compiler has no way to detect that that particular instance of None is of type Option[Option[A]], and you would be right.

It is probably an error in the scala implementation. Since algebraic data types are implemented with inheritance (that is Some[A] and None both inherit from Option[A]) one has to implement the method flatten separately in the two cases. Probably, nobody noticed (or cared) that in this way, one gets flatten also implemented on instances of None of the wrong option type.

Your implementation is fine :-)

@dom96 dom96 merged commit 7abad50 into nim-lang:devel Sep 27, 2017
bluenote10 pushed a commit to bluenote10/Nim that referenced this pull request Oct 21, 2017
* Add >>= operator to Options

* options.bind callback signature: A -> Option[B]

* Use `flatMap` as the name of the Option bind operation.

* Rename Options test "bind" to "flatMap"

* CR from @dom96: Remove spaces inside of check() call
@subsetpark subsetpark deleted the options-bind branch December 19, 2017 20:14
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

Successfully merging this pull request may close these issues.

8 participants