Skip to content
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

Allow SourceCodePrinter to show type without prefixes #9716

Closed
tgodzik opened this issue Sep 3, 2020 · 10 comments
Closed

Allow SourceCodePrinter to show type without prefixes #9716

tgodzik opened this issue Sep 3, 2020 · 10 comments
Assignees

Comments

@tgodzik
Copy link
Contributor

tgodzik commented Sep 3, 2020

While working with worksheets it turned out to be more useful to just use the show method on Type using it from macros like:

trait TPrint[T]{
  def render: String
}
object TPrint {
  inline given default[T] as TPrint[T] = ${ TypePrinter.typeString[T] }
}
object TypePrinter{
  def typeString[T](using ctx: QuoteContext, tpe: Type[T]): Expr[TPrint[T]] = {
    import ctx.tasty._
    val typePrinter = new SourceTypePrinter(ctx.tasty)(SyntaxHighlight.plain)
    '{  new TPrint[T]{ def render: String = ${ Expr(tpe.unseal.tpe.show) } }  }
  }
}

Unfortunately by default it prints the whole type with the prefix, so we would get scala.collection.immuntable.Listinstead of just List.

I was thinking of just adding an additional option to drop the prefixes there, which should not be much effort. Alternatively, we could reuse the PlainPrinter. However, while I feel confident I could work on adding an additional option I feel like consolidating the printers might be something I will not have the time to work on.

Any opinions? CC @odersky @bishabosha

@tgodzik tgodzik self-assigned this Sep 3, 2020
@nicolasstucki
Copy link
Contributor

The SourceCodePrinter is supposed to print the fully elaborated representation of the source code. Hence the prefix is necessary.

We could have a use second printing mode that shows a simpler representation of the code as with the Refined/PlainPrinter do. The difference is that this mode would not ensure that the code can be copy-pasted and recompiled (needed by decompiler), but this alternate mode is fine debugging purposes.

At the start, we used the RefinedPrinter but we had issues displaying deeply nested code.

@tgodzik
Copy link
Contributor Author

tgodzik commented Sep 7, 2020

The SourceCodePrinter is supposed to print the fully elaborated representation of the source code. Hence the prefix is necessary.

It makes sense! I don't that my particular use case was ever predicted.

We could have a use second printing mode that shows a simpler representation of the code as with the Refined/PlainPrinter do. The difference is that this mode would not ensure that the code can be copy-pasted and recompiled (needed by decompiler), but this alternate mode is fine debugging purposes.

Do you think a simple flag is enough (print prefix normally, but don't print with flag on) or should it be a separate class altogether like Refined vs Plain printer? I mnagaed to get the result I needed with removing just a bit of code and inlining it all for now.

@nicolasstucki
Copy link
Contributor

Maybe we can try that. The question there is: which prefixes can be elided?

  • scala.
  • scala.Predef.
  • Assume scala.collection.immuntable.List is equivalent to scala.Predef.List and then elide prefix. Same for similar aliases.
  • ...

@nicolasstucki
Copy link
Contributor

The flag should be enough

@smarter
Copy link
Member

smarter commented Sep 7, 2020

We have the same problem in error messages: they always print the fully qualified type which can sometimes makes them hard to read. The proper thing to do would be to take into account the imports available at the current point to know which prefixes can be elided, but no one has tried to implement that so far.

@prolativ
Copy link
Contributor

Before adding new functionality, we should probably make the current behaviour of SourceCodePrinter more consistent. I played with it a bit and I got quite surprised with the results I got.
(tested with dotr v0.27.0-RC1)

scala> import scala.tasty.reflect.SourceCodePrinter                                                          
     | import scala.quoted.show.SyntaxHighlight
     | import scala.quoted._
     | 
     | def showTypeImpl[T](using ctx: QuoteContext, tpe: Type[T]): quoted.Expr[String] = {
     |   import ctx.tasty._
     |   val typePrinter = new SourceCodePrinter(ctx.tasty)(SyntaxHighlight.plain)
     |   Expr(typePrinter.showTypeOrBounds(tpe.unseal.tpe))
     | }
     | 
     | inline def showType[T]: String = ${showTypeImpl[T]}
def showTypeImpl
  [T](using ctx: quoted.QuoteContext, tpe: quoted.Type[T]): quoted.Expr[String]
def showType[T] => String
scala> showType[List]                                                                                        
val res0: String = scala.List

scala> showType[scala.List]                                                                                  
val res1: String = scala.List

scala> showType[scala.collection.immutable.List]                                                             
val res2: String = [A >: scala.Nothing <: scala.Any] => scala.collection.immutable.List[A]

scala> showType[List[Int]]                                                                                   
val res3: String = scala.collection.immutable.List[scala.Int]

It looks like type aliases are expanded if type parameters are passed to them but not otherwise. Also type constructors are displayed as type lambdas but only if they're not type aliases. What should be the correct behaviour?

@nicolasstucki
Copy link
Contributor

nicolasstucki commented Sep 17, 2020

In general, we do not want to dealias to avoid losing some higher-level abstraction that may be simpler to read.

The alias for List and other aliases in Predef are special and we could print them either way. I would suggest printing scala.List instead of scala.collection.immutable.List as it is shorter (i.e. re-aliasing). Or keep the current behaviour which keep the aliasing present after typing.

Note that scala.List.type is a subtype of scala.collection.immutable.List.type (not the other way) which might behave differently in some corner-cases.

@prolativ
Copy link
Contributor

Dealiasing seems to be a problem also in other places and there have been already some attempts to solve this be realiasing in some contexts (see: https://contributors.scala-lang.org/t/pre-sip-realiasing-types-in-error-messages/4362/2). I'm not sure if something similar could be easily implemented in SourceCodePrinter as it uses a mutable buffer.
BTW showType[scala.List.type] returns scala.List instead of scala.List.type. Is this a bug or an expected behaviour?

@nicolasstucki
Copy link
Contributor

The best behavior is to print the types as the compiler typed it. In some situations the compiler will dealias some but not other. That is not part of what we control. This may change over time.

@tgodzik
Copy link
Contributor Author

tgodzik commented Dec 2, 2020

Fixed in #10172

We can now use showShort

@tgodzik tgodzik closed this as completed Dec 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants