scalapuzzlers / scalapuzzlers.github.com

Github Pages behind scalapuzzlers.com
www.scalapuzzlers.com
161 stars 53 forks source link

In search of one's true identity #120

Closed som-snytt closed 9 years ago

som-snytt commented 9 years ago
val vs = List(Some(42),None,Some(17))
vs flatMap (x => x)
vs flatMap identity

http://stackoverflow.com/a/29217414/1296806

demobox commented 9 years ago

@som-snytt: Thanks! I take it this is intended as a puzzler suggestion?

I'm getting this, which I guess is what you're referring to in the SO thread?

scala> val vs = List(Some(42),None,Some(17))
vs: List[Option[Int]] = List(Some(42), None, Some(17))

scala> vs flatMap (x => x)
res0: List[Int] = List(42, 17)

scala> vs flatMap identity
<console>:9: error: type mismatch;
 found   : Option[Int] => Option[Int]
 required: Option[Int] => scala.collection.GenTraversableOnce[?]
              vs flatMap identity
                         ^
som-snytt commented 9 years ago

Yes, I was too lazy to check if you already had a related puzzler, which you probably do, since mixing Option and collections comes up often. I thought an issue was a light-weight way of recording the idea for a future appendix or outtake reel. Or suggesting it if it turns into a suggestion.

demobox commented 9 years ago

I was too lazy to check if you already had a related puzzler, which you probably do

Well, not really, I'd say...it depends what we regard as the "puzzling" bit here. As far as I can make out, vs flatMap (x => x) only actually works because of the implicit option2Iterable, which allows the input x to be treated as an iterable, as required by the signature of flatMap:

  @SerialVersionUID(value = 0) final <synthetic> class anonfun$1 extends scala.r
untime.AbstractFunction1 with Serializable {
    final def apply(x: Option): Iterable = scala.this.Option.option2Iterable(x);

That obviously doesn't work for Predef.identity.

Was the insertion of the implicit the "puzzling piece" you had in mind here?

som-snytt commented 9 years ago

Backing up, did you not expect x => x and identity to be interchangeable?

Maybe that's just sloppy thinking, but it highlights how expected type makes all the diff.

demobox commented 9 years ago

Backing up, did you not expect x => x and identity to be interchangeable?

I would if the argument passed was indeed x => x. But what we're actually passing - probably without realizing it immediately - is x => option2iterable(x), which I wouldn't expect to be interchangeable with identity, no.

The "puzzle" here is then that the compiler can also use implicits within a function argument, I guess...or am I missing a comment you're trying to make here..?

som-snytt commented 9 years ago

OK, in that case, you're a better man than me. (Please don't test me on the prepositional use of "than".) I'd expect x => x, identity and identity(_) to do the same thing with the given expected type.

Is it possible that after years at this game, you have lost all empathy with the puzzled people?

Maybe you need a long journey into the Himalayas to restore your sense of the essential puzzlement underlying all human endeavor.

Or if you prefer, endeavour.

Alternatively, I must realize that everything I do is really stupid. But I thought I knew that already.

Wait, have I surpassed Socrates in realizing that I not only know that I know nothing, but I also know that I am entirely mistaken in my supposed knowledge that my knowledge is null and void?

demobox commented 9 years ago

I'd expect x => x, identity and identity(_) to do the same thing with the given expected type.

I would too. I'm not expressing myself clearly.

What I'm trying to say is that what you're passing to flatMap isn't actually x => x, because that wouldn't type check. As far as I can see, flatMap in this case expects something like Option[Int] => GenTraversableOnce[B], and if x is an Option[Int], then x => x is Option[Int] => Option[Int], so doesn't match the required type. So I wouldn't expect identity or identity(_) to work here either, since their type also doesn't match.

As far as I can make out, the only reason vs flatMap (x => x) actually compiles is because the compiler can resolve the mismatch between the type of x => x, i.e. Option[Int] => Option[Int], and the required type Option[Int] => GenTraversableOnce[B] is by applying the implicit conversion Option.option2iterable to the RHS of the function. I.e. the compiler implicitly converts the return value to option2iterable(x), which means that the function overall now has a type of Option[Int] => Iterable[Int], so type checks and can be passed to flatMap.

I guess it's worth asking why the compiler can't also apply that kind of implicit transformation to the function as a whole (i.e. "if f: Option[Int] => Option[Int] is given and Option[Int] => GenTraversableOnce[B] is expected and imp: Option[A] => Iterable[A] is available as an implicit conversion, then try the function imp ∘ f: Option[Int] => Iterable[Int]"), but would you say it's surprising that the compiler doesn't do that?

I hope that makes a bit more sense...and I'll gladly take you up on that Himalayan break ;-)

demobox commented 9 years ago

Going over this again, I would say this boils down to whether we feel that

val l: Iterable[Int] = Option(10)

compiling successfully is a puzzler or not.

@nermin: your thoughts..?

demobox commented 9 years ago

@nermin: Ping..?

demobox commented 9 years ago

@nermin: Ping..?

This one "got" Nermin, so I think we may have a puzzler here after all ;-)

demobox commented 9 years ago

@som-snytt: Discussion to be continued over at #133..? ;-)