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

Support String interpolator inline unapply #8577

Closed
nicolasstucki opened this issue Mar 20, 2020 · 9 comments · Fixed by #16358
Closed

Support String interpolator inline unapply #8577

nicolasstucki opened this issue Mar 20, 2020 · 9 comments · Fixed by #16358
Labels
area:inline area:metaprogramming:quotes Issues related to quotes and splices itype:enhancement Semester Project Good project to be done by an MSc or strong Bsc computer science student in one semester stat:taken This project is already worked on by a student or as part of another volunteership program
Milestone

Comments

@nicolasstucki
Copy link
Contributor

nicolasstucki commented Mar 20, 2020

To write a string interpolator with an apply and unapply. Both could be macros.

old code

In Scala 2 we would have written

implicit class XOps(s: StringContext) {
  object x {
    def apply(exprs: Any*) = macro ...
    def unapplySeq(...) = macro ...
  }
}

Possible encoding

A possible encoding in Dotty is

object XOps {
  opaque type StringContext = scala.StringContext
  def apply(sc: scala.StringContext): StringContext = sc
}
extension (inline ctx: StringContext) inline def x: XOps.StringContext = XOps(ctx)
extension (inline ctx: XOps.StringContext) inline def apply(inline exprs: Any*) = ${ ... }
extension (inline ctx: XOps.StringContext) inline def unapplySeq(...) = ${ ... }

This encoding ensures that both the StringContext and arguments are inlined and accessible by the macro.

The apply can work out of the box for x"...", even with a macro (see #8572).

But unapply is not taken into account, even without macros.

expectation

object XOps {
  opaque type StringContext = scala.StringContext
  def apply(sc: scala.StringContext): StringContext = sc
}
extension (inline ctx: StringContext) inline def x: XOps.StringContext = XOps(ctx)
extension (inline ctx: XOps.StringContext) inline def unapplySeq(arg: Any) = None

def test = 
  ??? match 
    case x"$a" => 

should compile.

Detailed description of the Semester project

Problem

We want to implement string interpolation extractors using macros:

def processSqlQuery(query: String) =
  query match
    case sql"select $col from $table" => println(s"You are selecting $col from $table")

The problem is that the old, Scala 2, encoding involves passing StringContext, which is an argument to the implicit class, to the macro that implements the extractor. We need to pass StringContext because it contains all the information on the string being extracted, and the macro needs that information to be useful. However, being a constructor argument to a class, StringContext is not statically known. Hence, it cannot be passed to a macro since being statically known is a requirement for (inlined) macro arguments.

Solution

Investigate possible encodings for the string interpolator macro-based extractors. The output of the project will be a report on the encodings investigated and why they can or cannot be used. Ideally, if a good solution is found, you can also implement it as part of the semester project, but this is not mandatory.

@nicolasstucki
Copy link
Contributor Author

nicolasstucki commented Mar 20, 2020

Requires #8530

@nicolasstucki
Copy link
Contributor Author

nicolasstucki commented Mar 20, 2020

And requires lampepfl/dotty-feature-requests#112 (or a fix targeted to sting interpolators)

@odersky
Copy link
Contributor

odersky commented Jun 2, 2020

This would mean we need to allow unapply as an extension method.

@nicolasstucki nicolasstucki added this to the 3.1.0 milestone Nov 5, 2020
@nicolasstucki nicolasstucki modified the milestones: 3.1.0, 3.2.0 Aug 24, 2021
@anatoliykmetyuk anatoliykmetyuk modified the milestones: 3.2.0, 3.3.0 Jan 10, 2022
@anatoliykmetyuk anatoliykmetyuk added the Semester Project Good project to be done by an MSc or strong Bsc computer science student in one semester label Jan 13, 2022
@dwijnand
Copy link
Member

This would mean we need to allow unapply as an extension method.

For general unapplies, I mean when used with a match it seems to work already:

class Foo(val x: Int, val y: Int)
object Foo { val default = new Foo(0, 0) }

extension (F: Foo.type) def unapply(x: Foo): (Int, Int) = (x.x, x.y)

@main def test(): Unit = new Foo(2, 3) match
  case Foo(x, y) => println(x + y)
$ scala -3.head foo.scala
5

@nicolasstucki
Copy link
Contributor Author

nicolasstucki commented Jan 17, 2022

Another idea would be to allow extension modules to give prefixes to extension methods.

extension (inline sc: StringContext) object x:
  inline def apply(elems: Any*) = ...
  inline def unapplySeq() = ...

Those objects can only contain methods and can not extend anything.

Not sure how this would be encoded.

// in a patten
x"abc$d"
// is desugarred as 
StringContext("abc").x.apply(d) 
// is typed as 
x.apply(StringContext("abc"))(d)

It should end up desugared into something like this probably.

object x:
  inline def apply(inline sc: StringContext)(elems: Any*) = ...
  inline def unapplySeq(inline sc: StringContext)() = ...

@soronpo
Copy link
Contributor

soronpo commented Jan 17, 2022

Just to mention that my current workaround is:

extension (inline sc: StringContext)
  transparent inline def b: Any = ${ SIParts.scMacro[BParts]('sc) }
class BParts[P <: Tuple](parts: P):
  transparent inline def apply(inline args: Any*): Any = ${ applyMacro('parts, 'args) }
  transparent inline def unapplySeq(inline arg: Any): Option[Seq[Any]] = ${ unapplySeqMacro('parts, 'arg) }
// P is a tuple representation of the SC parts
trait SIParts[P <: Tuple](parts: P)
object SIParts:
  def scMacro[SI[_ <: Tuple]](sc: Expr[StringContext])(using
      Quotes,
      Type[SI]
  ): Expr[Any] =
    import quotes.reflect.*
    val '{ StringContext(${ Varargs(args) }*) } = sc
    val tplExpr = Expr.ofTupleFromSeq(args)
    val tplTpe = tplExpr.asTerm.tpe
    val tplType = tplTpe.asTypeOf[Tuple]
    val AppliedType(siTpe, _) = TypeRepr.of[SI[Tuple]]
    val siSym = siTpe.typeSymbol
    val siTree =
      New(siTpe.asTypeTree)
        .select(siSym.primaryConstructor)
        .appliedToType(tplTpe)
        .appliedTo(tplExpr.asTerm)
    siTree.asExpr
  end scMacro

@anatoliykmetyuk
Copy link
Contributor

Pietro Gorilskij would probably take this as a MSc project during the Spring semester.

@anatoliykmetyuk anatoliykmetyuk added the stat:taken This project is already worked on by a student or as part of another volunteership program label Jan 24, 2022
@odersky
Copy link
Contributor

odersky commented Apr 5, 2022

Is this being worked on?

@anatoliykmetyuk
Copy link
Contributor

anatoliykmetyuk commented Apr 6, 2022

Yes, this project is taken by Pietro Gorilskij @gorilskij.

nicolasstucki pushed a commit to dotty-staging/dotty that referenced this issue Nov 17, 2022
Fixes the computation of the inline unapply temporary unanimous unapply placeholder.

Fixes scala#8577
nicolasstucki pushed a commit to dotty-staging/dotty that referenced this issue Nov 17, 2022
Fixes the computation of the inline unapply temporary unanimous unapply placeholder.

Fixes scala#8577
Fixes scala#15188
nicolasstucki pushed a commit to dotty-staging/dotty that referenced this issue Nov 18, 2022
Fixes the computation of the inline unapply temporary unanimous unapply placeholder.

Fixes scala#8577
Fixes scala#15188
nicolasstucki added a commit to dotty-staging/dotty that referenced this issue Dec 7, 2022
* Fixes the computation of the inline unapply temporary unanimous unapply placeholder
* Handle leading given parameters in inline unapplies

Fixes scala#8577
Fixes scala#12991
Fixes scala#15188
nicolasstucki added a commit that referenced this issue Dec 7, 2022
little-inferno pushed a commit to little-inferno/dotty that referenced this issue Jan 25, 2023
* Fixes the computation of the inline unapply temporary unanimous unapply placeholder
* Handle leading given parameters in inline unapplies

Fixes scala#8577
Fixes scala#12991
Fixes scala#15188
@Kordyjan Kordyjan modified the milestones: 3.3.0-RC1, 3.3.0 Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area:inline area:metaprogramming:quotes Issues related to quotes and splices itype:enhancement Semester Project Good project to be done by an MSc or strong Bsc computer science student in one semester stat:taken This project is already worked on by a student or as part of another volunteership program
Projects
None yet
6 participants