Closed atrigent closed 1 year ago
just two equivalent ways
This is a misunderstanding. Using https://github.com/scala/scala/pull/10309 with -Xlint:eta-impure
:
scala> objs.head.method _
<console>:13: warning: impure expression as part of an eta-expansion. The expression is evaluated eagerly, before the function is created, not when the function is evaluated.
objs.head.method _
^
<console>:13: warning: inferred existential type _1 => scala.collection.immutable.Set[_1] forSome { type _1 >: String with Int }, which cannot be expressed by wildcards, should be enabled
by making the implicit value scala.language.existentials visible.
This can be achieved by adding the import clause 'import scala.language.existentials'
or by setting the compiler option -language:existentials.
See the Scaladoc for value scala.language.existentials for a discussion
why the feature should be explicitly enabled.
objs.head.method _
^
res0: _1 => scala.collection.immutable.Set[_1] forSome { type _1 >: String with Int } = $Lambda$2123/0x00000008015c3990@5f6a8efe
scala> objs.head.method(_)
res1: String with Int => scala.collection.immutable.Set[_ >: String with Int] = $Lambda$2140/0x00000008015c75e8@407f2029
scala>
Using -Vprint:typer
,
private[this] val res0: _1 => scala.collection.immutable.Set[_1] forSome { type _1 >: String with Int } = {
<synthetic> val eta$0$1: MyTrait[_1] = $line6.$read.INSTANCE.$iw.$iw.objs.head;
((v: _1) => eta$0$1.method(v))
};
<stable> <accessor> def res0: _1 => scala.collection.immutable.Set[_1] forSome { type _1 >: String with Int } = $iw.this.res0
The subexpression is evaluated first when eta-expanded, in the first example.
In the second case, the placeholder syntax is "just syntax" for x => objs.head.method(x)
.
Obviously (so to speak), the semantics are entirely different.
I hope Lukas is inspired to get something like this lint merged.
Seth may intervene with, "Please ask questions on the forum and reserve the bug tracker for when you're sure it's a bug."
You did ask, and I was about to answer but was distracted.
Exactly how the REPL reports the type is the topic at https://github.com/lampepfl/dotty/issues/17032 where I ask for the more informative representation.
I think you're asking, Can't it just dummy it down a bit since we know it's really something like Int => Set[Int]
or like dotty says
scala> objs.head.method _
val res0: Int & String => Set[? >: Int & String <: Int | String] = Lambda$1597/0x00000008015185e8@5cc669d
Thanks for the quick response.
I do see that doing { val tmp = objs.head; tmp.method(_) }
also produces the same existential type. With -Vprint:typer
(thanks for telling me about that, I'll definitely be using it more), I see:
private[this] val res14: _1 => scala.collection.immutable.Set[_1] forSome { type _1 >: String with Int } = {
val tmp: MyTrait[_ >: String with Int] = $read.this.$line6$read.$iw.objs.head;
((x$1: _1) => tmp.method(x$1))
};
...which is pretty similar to what you get with objs.head.method _
. But, I'm still not quite getting why the type is different. Especially since, in the above snippet, tmp
is a MyTrait[_ >: String with Int]
. So the type parameter is _ >: String with Int
, meaning the method return type should be Set[_ >: String with Int]
, shouldn't it?
I also noticed that { val tmp = objs.head; tmp.method _ }
throws an error?
error: type mismatch;
found : _1(in value tmp)
required: (some other)_1(in value tmp)
yet another thing I don't understand...
Also, when I said "just two equivalent ways" I was referring to the types, not the way that the function was created. According to some things I've read, it sounds like these following types might actually be equivalent. Is that wrong?
_1 => scala.collection.immutable.Set[_1] forSome { type _1 >: String with Int }
String with Int => scala.collection.immutable.Set[_ >: String with Int]
They don't look equivalent to me — in the first, the function's input type is existential, in the second, it isn't (it's just String with Int
).
Seth may intervene with, "Please ask questions on the forum and reserve the bug tracker for when you're sure it's a bug."
indeed, in general we ask that folks not use the bug tracker for questions, but use https://users.scala-lang.org . come to the bug tracker when you believe you have a solid case that there is a previously unreported bug and not just some unexpected behavior that surprised you
There are a number of previous tickets here in the tracker about existentials and/or type inference, perhaps there are relevant ones? And perhaps there's language in the Scala spec which would help distinguish "bug" from "unexpected behavior" here.
They don't look equivalent to me — in the first, the function's input type is existential, in the second, it isn't (it's just
String with Int
).
That did occur to me - the one with the existential could be read as saying that every instance of _1
is the same type (that satisfies some constraint), while the second one would be that each of those types satisfies the constant, but they aren't necessarily the same type. It is a little bit ambiguous whether the forSome
is grouped just with the return type or with the entire function type.
The former interpretation does seem to be the more correct one for this function, so why doesn't it use that type when I do objs.head.method(_)
?
indeed, in general we ask that folks not use the bug tracker for questions, but use https://users.scala-lang.org
Ok, I will try this next time.
There are a number of previous tickets here in the tracker about existentials and/or type inference, perhaps there are relevant ones? And perhaps there's language in the Scala spec which would help distinguish "bug" from "unexpected behavior" here.
I wouldn't really know where to start to find something like this. Does anyone have any further insight?
This question
The former interpretation does seem to be the more correct one for this function, so why doesn't it use that type when I do
objs.head.method(_)
?
suggests you re-read my previous comment. If I write xs.map(expr.f(_))
then my expr
may evaluate to a wildly different value every time it is invoked. Because of subtyping, you don't know if you're calling f
on a Foo
or a Bar
.
On using github search on scala/bug/issues, "existential" is one of the easier search terms, but usually it is a challenge. Usually it is more like a dialogue with ChatGPT.
Closing as "not a bug" because if there is a documentation issue, it is out of scope.
Reproduction steps
Scala version: 2.13.10
Given:
Problem
In REPL:
I admittedly don't know for sure whether this is a bug or not. My intuition says that these should be the same function, and my understanding is that these are just two equivalent ways of describing a function type involving existentials, but maybe there's some subtle difference I can't see.