10000 Simplify type bounds. by paulp · Pull Request #2417 · scala/scala · GitHub
[go: up one dir, main page]

Skip to content

Simplify type bounds. #2417

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

Merged
merged 1 commit into from
Apr 23, 2013
Merged

Simplify type bounds. #2417

merged 1 commit into from
Apr 23, 2013

Conversation

paulp
Copy link
Contributor
@paulp paulp commented Apr 19, 2013

I started out looking to limit the noise from empty type
bounds, i.e. the endless repetition of

  class A[T >: _root_.scala.Nothing <: _root_.scala.Any]

This led me to be reminded of all the unnecessary and
in fact damaging overreaches which are performed during parsing.
Why should a type parameter for which no bounds are
specified be immediately encoded with this giant tree:

  TypeBounds(
    Select(Select(Ident(nme.ROOTPKG), tpnme.scala_), tpnme.Nothing),
    Select(Select(Ident(nme.ROOTPKG), tpnme.scala_), tpnme.Any)
  )

...which must then be manually recognized as empty type bounds?
Truly, this is madness.

  • It deftly eliminates the possibility of recognizing
    whether the user wrote "class A[T]" or "class A[T >: Nothing]"
    or "class A[T <: Any]" or specified both bounds. The fact
    that these work out the same internally does not imply the
    information should be exterminated even before parsing completes.
  • It burdens everyone who must recognize type bounds trees,
    such as this author
  • It is far less efficient than the obvious encoding
  • It offers literally no advantage whatsoever

Encode empty type bounds as

  TypeBounds(EmptyTree, EmptyTree)

What could be simpler.

@paulp
Copy link
Contributor Author
paulp commented Apr 20, 2013

Review by @retronym

@retronym
Copy link
Member

Looks like a winner to me, but I might run it past the Scala meeting on Tuesday to see there are any arguments in defence of the status quo.

if (defined.nonEmpty)
t setPos wrappingPos(defined)
else
t setPos o2p(in.offset)
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the difference between start, end and in.offset? I guess in.offset changes, but still why are start and end saved and not used?

Copy link
Contributor

Choose a reason for hiding this comment

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

Just a minor nitpick: Is saving three characters worth picking lo/hi over low/high`?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Or put another way, is adding three characters worth gratuitously making up different names for constructs which already have names? "lo" and "hi" are what they are called.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

start and end are residue from the great deal of flailing it took before I managed to get this tree past the position validator. I'll remove them.

Copy link
Contributor

Choose a reason for hiding this comment

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

After losing start and end, the ='s could be aligned further left if there were a 2-letter form for "defined". My brother is an avid Scrabble player and keeps a list of meaningful 2-letter combinations, with surprising results.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wonder if that means you don't know about this... http://www.scrabble-assoc.com/ratings/state/co.html

(You have to scroll all the way down to #4.)

Copy link
Contributor

Choose a reason for hiding this comment

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

Then there's no excuse.

I would expect all the ='s to line up following seven-letter identifiers.

@Blaisorblade
Copy link
Contributor

Just to be sure, you still canonicalize empty bounds, but only from Typers on, right? In general, the phases before canonicalization need to handle more equivalent or almost equivalent trees, so there are a few more risks for bugs. OTOH, GHC is proud to bring this even further for better error reporting — it performs type inference before desugaring exactly for this reason, so that it never needs to show the user code that the user didn't write.

@paulp
Copy link
Contributor Author
paulp commented Apr 20, 2013

@Blaisorblade This is the parser. It is not performing canonicalization; if you enter "A >: Nothing <: Any" that is what will be encoded. It is simply not inventing bounds.

I started out looking to limit the noise from empty type
bounds, i.e. the endless repetition of

  class A[T >: _root_.scala.Nothing <: _root_.scala.Any]

This led me to be reminded of all the unnecessary and
in fact damaging overreaches which are performed during parsing.
Why should a type parameter for which no bounds are
specified be immediately encoded with this giant tree:

  TypeBounds(
    Select(Select(Ident(nme.ROOTPKG), tpnme.scala_), tpnme.Nothing),
    Select(Select(Ident(nme.ROOTPKG), tpnme.scala_), tpnme.Any)
  )

...which must then be manually recognized as empty type bounds?
Truly, this is madness.

 - It deftly eliminates the possibility of recognizing
   whether the user wrote "class A[T]" or "class A[T >: Nothing]"
   or "class A[T <: Any]" or specified both bounds. The fact
   that these work out the same internally does not imply the
   information should be exterminated even before parsing completes.
 - It burdens everyone who must recognize type bounds trees,
   such as this author
 - It is far less efficient than the obvious encoding
 - It offers literally no advantage whatsoever

Encode empty type bounds as

  TypeBounds(EmptyTree, EmptyTree)

What could be simpler.
@Blaisorblade
Copy link
Contributor

@paulp
I think I didn't make myself clear. Reading the code more closely, I see that indeed, with your change, the parser stopped "inventing bounds", but this is now done by the typer. Now, "inventing bounds" is the canonicalization I was talking about, and it's a canonicalization since it's a pure function which turns inputs which are different but equivalent into the same output.

And my point is that whenever you change the parser to canonicalize less equivalent constructs in the source, which is something you want to do, later phases have to be more careful, since they'll have to deal with trees which were impossible before.

Probably the extra cost is still worth the more accurate error reporting, but I guess that's not for me to say since I don't know the code well enough.

@paulp
Copy link
Contributor Author
paulp commented Apr 20, 2013

Oh, my comment from my first version of this PR #2405 didn't make the jump. "This will impact anyone who parses TypeBoundsTrees and never expects to see an EmptyTree in there. I don't know what can be done about that beyond alerting/preparing the usual parties." So I understand it makes trees possible which weren't possible before.

@paulp
Copy link
Contributor Author
paulp commented Apr 20, 2013

I'm not sure anyone can call the typer a "pure function" with too straight a face. BTW you already had to deal with a potential wide range of untyped trees, which I'm sure nobody did, because they had to look for (at minimum) all of these:

Select(Select(Ident(nme.ROOTPKG), tpnme.scala_), tpnme.Nothing)
Select(Ident(tpnme.scala_), tpnme.Nothing)
Ident(tpnme.Nothing)

And you can't even know if that Nothing is the real Nothing and not some other type called Nothing until it's typed. Not to mention type aliases, etc. etc. etc. You just don't know what the bounds are until it is typed.

8000

@Blaisorblade
Copy link
Contributor

OK, so the parser was really not canonicalizing so much in a useful way.
Well, I'll leave you to work :-)

@retronym
Copy link
Member

LGTM, and to the Scala meeting today.

retronym added a commit that referenced this pull request Apr 23, 2013
@retronym retronym merged commit cd148d9 into scala:master Apr 23, 2013
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.

5 participants
0