Quill use-cases for Quoted Matching Update

After a short break for Passover, I am back and attempting to implement SchemaMeta for Dotty-Quill, this requires more usage of Quoted Matching in some new ways and revisits some current issues.

  • My only real show-stopper is this: https://github.com/lampepfl/dotty/issues/8745.
    It involves an issue where Dotty throws a Class Cast Exception whenever one attempts to match a method on a trait T that was then extended into an object O extends T and statically imported via import O._. This is how schemaMeta, queryMeta, insertMeta and updateMeta all work. It’s very important to get this fixed otherwise everyone is going to have to do QueryDsl.schemaMeta instead of schemaMeta everywhere.

  • Quoted Matching is generally nicer then quasi-quotes but certain use-cases are not possible by design. For example, quasi-quotes allowed the matching of aribtrary selections case q"$a.$b" => without requiring any knowledge of what $a's type, we use this in Quill when parsing Query-Schema property mappings. This kind of functionality is intentially prohibited with Quoted Matching because it offers zero type safety (i.e. how the heck do you know $b is available on $a!?) however, the underlying Tasty-Reflect API can still be used to get this kind of behavior e.g: case Select(a, b) =>, you just need to unseal the Expr[_] into a Term first.

  • I initially expected Quoted Matching to allow the same kinds of sugar as standard Scala. For example, matching o.map(func) (where o is a Option[T]) could be done either in via the regular

    case '{ $o.map[$t]($func) }` =>

    as well as the shortened:

    case '{ $o.map($func) }` =>

    With the advent of #8695 this is finally possible! This is very exciting since the requirment of specifying all types is very unintuitive.

  • Matching a higher-order function as well as it’s lambda argument is currently not possible with Quoted Matching.

    case '{ (o: Option[String]).map[$ot](($x: String) => $out) } => // does not match

    Whereas using qusai-quotes it is:

    case q"o.map($x => $out)" => // works!

    Additionally, using the $x variable in a quoted match causes a Not-Found exception. I think this behavior is a bug: #8749.
    This is not a show-stopper because I can work around it using the Lambda(...) matcher from Tasty-Reflect or matching on a DefDef tree directly.

  • Re-declaring a type via a type-ascription that has already been defined will cause an exception. For example, matching this:

    object FunObject { def fun[T](t: T => String) = t }
    matchMacro(fun((x: String) => x.toUpperCase))

    … by doing this:

    case '{ FunObject.fun[$tt](($arg: $ttt) => $out) } =>

    Will blow up because $arg is of type $tt, not $ttt!

    @bishabosha had a great solution for this issue:

    case 'type $tt; { FunObject.fun[`$tt`](($arg: `$tt`) => $out) } =>

    I would use this pattern throughout my codebase if it wasn’t for #8749.

  • In general, Quoted Matching is still rough around the edges and needs a little bit of production hardening. It is still relatively easy to get it to break (e.g. see my first issue #8745) or to freeze up the compiler e.g: #8746. However, I still firmly believe that it is a substantial improvement over quasi-quotes.

Thank you @biboudis, @liufengyun, @nicolasstucki, and @smarter for all of the assistance you have provided me up to this point as well as for your continuing help. It has been quite a challenge re-architecting Quill to Dotty paradigms but the light at the end of the tunnel is brighter every day!


Great work.

Quill is one of the most macro heavy libraries in the Scala ecosystem; successfully porting it over to Scala 3 will help other Scala 2 macro based projects follow suit.

Once the Quill >> Scala 3 migration is complete then begins the next task: porting Quill to Scala.js and finding a way to hook into native drivers for SQLite on Android and iOS :sweat_smile:

1 Like

I’m glad that Quill issues are getting fixed.

We have several blockers for complete izumi-reflect implementation (#8520, #8514, #8764). I really hope they can be fixed in foreseeable future.

By the way, right now our last release correctly handles =:= and <:< works for many simple cases when no type constructors with variant argument are involved.

1 Like