Skip to content

Add refined tupled records prototype #7731

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 10 commits into from
Dec 20, 2019

Conversation

biboudis
Copy link
Contributor

@biboudis biboudis commented Dec 12, 2019

This PR adds two macros, toTuple and fromTuple that transform a refinement type into a tuple and vice-versa:

  type Person = Record {
    val name: String
    val age: Int
  }

  val person: Person = Record("name" -> "Emma", "age" -> 42).asInstanceOf[Person]
  val res: (("name", String), ("age", Int)) = person.toTuple
  val res2: Person = Record.fromTuple(res)

To enable this functionality the user would need to SelectableRecord instead of Selectable, e.g., :

  case class Record(elems: (String, Any)*) extends SelectableRecord {
    def selectDynamic(name: String): Any = elems.find(_._1 == name).get._2
  }

  object Record extends SelectableRecordCompanion[Record] {
    def fromUntypedTuple(elems: (String, Any)*): Record = Record(elems: _*)
  }

@biboudis biboudis self-assigned this Dec 12, 2019
@biboudis biboudis marked this pull request as ready for review December 13, 2019 17:18
@biboudis biboudis requested a review from odersky December 13, 2019 17:18
@biboudis biboudis changed the title Add basic structure for refined selectable macros Add refined tupled records Dec 13, 2019
@nicolasstucki nicolasstucki self-assigned this Dec 14, 2019
@nicolasstucki nicolasstucki removed their assignment Dec 17, 2019
@nicolasstucki nicolasstucki requested review from nicolasstucki and removed request for odersky December 17, 2019 12:21
@milessabin
Copy link
Contributor

I'm not super-keen on the aesthetics of the asInstanceOf[Person] in,

val person: Person = Record("name" -> "Emma", "age" -> 42).asInstanceOf[Person]

I appreciate that we need to mention Person somewhere, but can't we drop the asInstanceOf?

Could,

val person: Person = Record("name" -> "Emma", "age" -> 42).as[Person]

be made to work?

@nicolasstucki
Copy link
Contributor

Probably a record should have a better constructor. One that could figure out the refined type.

@biboudis
Copy link
Contributor Author

@nicolasstucki do you mean a smarter implementation of the constructor or something like Record[Person]. In that case I think the as reveals the intent nicely.

@nicolasstucki
Copy link
Contributor

I meant Record("name" -> "Emma", "age" -> 42) would return Record{val name: String; val age: Int which is a person

@biboudis
Copy link
Contributor Author

After a quick brainstorming with @liufengyun we propose some functionality on tasty-reflect like the following where qctx.expected is something that the macro system provides. WDYT?

  object Record extends SelectableRecordCompanion[Record] {
    def fromUntypedTuple(elems: (String, Any)*): Record = Record(elems: _*)

    inline def apply(elems: (String, Any)*) <: Record = ${ applyImpl('elem)) }

    def applyImpl(elems: Expr[Seq[(String, Any)]])(given qctx: QuoteContext) = {
      type TT

      implicit val ttype: quoted.Type[TT] = qctx.expected.seal.asInstanceOf[quoted.Type[TT]]
      
      '{ new Record(elems).asInstanceOf[TT] }
    }
  }

😃 😃

@nicolasstucki
Copy link
Contributor

That is horribly unsound

@nicolasstucki
Copy link
Contributor

You probably want something in the lines of

inline def apply[R <: Record](elems: (String, Any)*) <: R = ${ applyImpl('elem, '[R])) }

@biboudis
Copy link
Contributor Author

I assumed we wanted to avoid the type parameter 😛! apply[R] sounds good!

@julienrf
Copy link
Contributor

I appreciate that we need to mention Person somewhere, but can't we drop the asInstanceOf?

Why can’t the following work?

val person: Person = Record("name" -> "Emma", "age" -> 42)

@biboudis
Copy link
Contributor Author

biboudis commented Dec 17, 2019

Hmm the compiler cannot infer this ATM 🤷‍♂ I guess it would need some tweaking in the Typer.

@nicolasstucki
Copy link
Contributor

No, the apply method would just be another macro

@nicolasstucki
Copy link
Contributor

We are getting off-topic. This PR is about prototyping the core macro logic for refinement types. Any API design concerns are for later.

@biboudis
Copy link
Contributor Author

After the last commit I have two observations to share:

  1. @anatoliykmetyuk typeCheckErrors seems to include the same error many times on the returned list. I don't know if this is by design because in the check file we have the same error twice as well typeCheckErrors.check. WDYT?
  2. @nicolasstucki the inlined apply works when I pass Person explicitly as the type argument of the constructor call Record[Person]("name" -> "Emma", "age" -> 42)

If I don't, the compiler infers Nothing (which later splices in the asInstanceOf). This may be an inference problem instead of a macro problem.

@anatoliykmetyuk
Copy link
Contributor

anatoliykmetyuk commented Dec 17, 2019

Yes, typeCheckErrors may have the same error many times. That's because it takes raw error list from the reporters of the parser and typer phases and that's what the reporters have. I don't know why precisely they may have duplicates so I didn't do any filtering.

@anatoliykmetyuk
Copy link
Contributor

The only filtering done in typeCheckErrors is to exclude parser errors from typer errors:

https://github.com/lampepfl/dotty/blob/d3a2d61d531aa88532bd9d37392f960c5477b705/compiler/src/dotty/tools/dotc/typer/Inliner.scala#L222

@biboudis
Copy link
Contributor Author

biboudis commented Dec 18, 2019

If I don't, the compiler infers Nothing (which later splices in the asInstanceOf). This may be an inference problem instead of a macro problem.

Opened #7798

//edit: seems that after #7798 is solved (related to inlining), the same type inference problem arises even without inline #7805

@nicolasstucki nicolasstucki changed the title Add refined tupled records Add refined tupled records prototype Dec 20, 2019
@nicolasstucki nicolasstucki merged commit d935be6 into scala:master Dec 20, 2019
@nicolasstucki nicolasstucki deleted the refined-selectable branch December 20, 2019 12:24
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