-
-
Save lbialy/b3c969824f9c6f612c1776da817d0f3a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.quoted.* | |
extension (using quotes: Quotes)(repr: quotes.reflect.TypeRepr) | |
def flatMapResultType(f: quotes.reflect.TypeRepr => Option[quotes.reflect.TypeRepr]): Option[quotes.reflect.TypeRepr] = | |
import quotes.reflect.* | |
repr match | |
case MethodType(paramNames, paramTypes, resultType) => | |
resultType.flatMapResultType(f).map(tp => MethodType(paramNames)(_ => paramTypes, _ => tp)) | |
// case PolyType => // should not appear here | |
case tp => | |
f(tp) | |
def refineType(using quotes: Quotes)(base: quotes.reflect.TypeRepr, refinements: Seq[(String, quotes.reflect.TypeRepr)]): quotes.reflect.TypeRepr = | |
import quotes.reflect.* | |
refinements match | |
case Nil => base | |
case (label, info) :: tail => | |
val newBase = Refinement(base, label, info) | |
refineType(newBase, tail) | |
def optionViewRefinements[A : Type](using quotes: Quotes)(tryTransformType: quotes.reflect.TypeRepr => Option[quotes.reflect.TypeRepr]): Seq[(String, quotes.reflect.TypeRepr)] = | |
import quotes.reflect.* | |
def isPolyType(repr: TypeRepr) = repr.widen match | |
case _: PolyType => true | |
case _ => false | |
val typeSym = TypeRepr.of[A].typeSymbol | |
val fieldRefinements: Seq[(String, quotes.reflect.TypeRepr)] = for | |
member <- typeSym.fieldMembers | |
tpe <- tryTransformType(member.termRef.widen) | |
yield (member.name, tpe) | |
val methodRefinements = for | |
member <- typeSym.methodMembers | |
tpe0 = member.termRef.widen | |
if !isPolyType(tpe0) | |
tpe <- tpe0.flatMapResultType(tryTransformType) | |
asMethodTpe = tpe match | |
case x: MethodOrPoly => x | |
case x => ByNameType(x) | |
yield (member.name, asMethodTpe) | |
fieldRefinements ++ methodRefinements | |
end optionViewRefinements |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//> using scala 3.3.2 | |
class Foo: | |
val value = Some("abc") | |
def methodNoParams = Some(true) | |
def methodEmptyParens() = Some(0) | |
def methodEmptyParensUnit() = println("Hello") | |
def methodSingleArg(i: Int) = Some(i + 1) | |
def methodTwoArgs(i: Int, j: Int) = Some(i + j) | |
def methodCurried(i: Int)(j: Int) = Some(i + j) | |
val opt = Some(new Foo) | |
@main def run() = { | |
println(opt.?.value) | |
println(opt.??.value) | |
println(opt.?.methodNoParams) | |
println(opt.??.methodNoParams) | |
println(opt.?.methodEmptyParens()) | |
println(opt.??.methodEmptyParens()) | |
println(opt.?.methodSingleArg(0)) | |
println(opt.??.methodSingleArg(0)) | |
println(opt.?.methodTwoArgs(1, 2)) | |
println(opt.??.methodTwoArgs(1, 2)) | |
// TODO: Handle curried functions | |
// println(opt.?.methodCurried(1)(2)) | |
// println(opt.??.methodCurried(1)(2)) | |
// TODO: Handle partially applied methods | |
// println(opt.?.methodEmptyParens) | |
// println(opt.??.methodEmptyParens) | |
// println(opt.?.methodCurried(1)) | |
// println(opt.??.methodCurried(1)) | |
// TODO: Handle foreach | |
// opt.?!.methodEmptyParensUnit() | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.quoted.* | |
class OptionFlatMapView[+A](opt: Option[A]) extends Selectable: | |
transparent inline def selectDynamic(inline name: String) = ${ optionFlatMapViewSelectDynamicImpl('{opt}, '{name}) } | |
transparent inline def applyDynamic(inline name: String)(inline args: Any*) = ${ optionFlatMapViewApplyDynamicImpl('{opt}, '{name}, '{args}) } | |
def optionFlatMapViewSelectDynamicImpl[A : Type](opt: Expr[Option[A]], name: Expr[String])(using quotes: Quotes): Expr[Any] = | |
import quotes.reflect.* | |
def selectName(inner: Expr[A]) = | |
Select.unique(inner.asTerm, name.valueOrAbort).asExprOf[Option[Any]] | |
'{ ${opt}.flatMap(inner => ${selectName('{inner})}) } | |
def optionFlatMapViewApplyDynamicImpl[A : Type](opt: Expr[Option[A]], name: Expr[String], args: Expr[Seq[Any]])(using quotes: Quotes): Expr[Any] = | |
import quotes.reflect.* | |
def applyArgsToName(inner: Expr[A], argsExpr: Expr[Seq[Any]]) = | |
val argExprs = argsExpr match | |
case Varargs(args) => | |
args.map(_.asTerm).toList | |
Select.unique(inner.asTerm, name.valueOrAbort).appliedToArgs(argExprs).asExprOf[Option[Any]] | |
'{ ${opt}.flatMap(inner => ${applyArgsToName('{inner}, args)}) } | |
trait OptionFlatMapViewProvider[A]: | |
type View <: OptionFlatMapView[A] | |
def view(opt: Option[A]): View | |
object OptionFlatMapViewProvider: | |
transparent inline given optionFlatMapViewProvider[A]: OptionFlatMapViewProvider[A] = ${ optionFlatMapViewProviderImpl[A] } | |
def optionFlatMapViewProviderImpl[A : Type](using quotes: Quotes): Expr[OptionFlatMapViewProvider[A]] = | |
import quotes.reflect.* | |
val refinements = optionViewRefinements { tp => | |
tp.asType match | |
case '[Option[t]] => Some(TypeRepr.of[Option[t]]) | |
case _ => None | |
} | |
val refinedType = refineType(TypeRepr.of[OptionFlatMapView[A]], refinements).asType | |
refinedType match | |
case '[t] => '{ new OptionFlatMapViewProvider[A] { | |
override type View = t | |
override def view(opt: Option[A]): View = new OptionFlatMapView(opt).asInstanceOf[t] | |
}.asInstanceOf[OptionFlatMapViewProvider[A] { type View = t }] } | |
extension [A](opt: Option[A])(using p: OptionFlatMapViewProvider[A]) | |
def ?? : p.View = p.view(opt) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import scala.quoted.* | |
class OptionMapView[+A](opt: Option[A]) extends Selectable: | |
transparent inline def selectDynamic(inline name: String) = ${ optionMapViewSelectDynamicImpl('{opt}, '{name}) } | |
transparent inline def applyDynamic(inline name: String)(inline args: Any*) = ${ optionMapViewApplyDynamicImpl('{opt}, '{name}, '{args}) } | |
def optionMapViewSelectDynamicImpl[A : Type](opt: Expr[Option[A]], name: Expr[String])(using quotes: Quotes): Expr[Any] = | |
import quotes.reflect.* | |
def selectName(inner: Expr[A]) = | |
Select.unique(inner.asTerm, name.valueOrAbort).asExprOf[Any] | |
'{ ${opt}.map(inner => ${selectName('{inner})}) } | |
def optionMapViewApplyDynamicImpl[A : Type](opt: Expr[Option[A]], name: Expr[String], args: Expr[Seq[Any]])(using quotes: Quotes): Expr[Any] = | |
import quotes.reflect.* | |
def applyArgsToName(inner: Expr[A], argsExpr: Expr[Seq[Any]]) = | |
val argExprs = argsExpr match | |
case Varargs(args) => | |
args.map(_.asTerm).toList | |
Select.unique(inner.asTerm, name.valueOrAbort).appliedToArgs(argExprs).asExprOf[Any] | |
'{ ${opt}.map(inner => ${applyArgsToName('{inner}, args)}) } | |
trait OptionMapViewProvider[A]: | |
type View <: OptionMapView[A] | |
def view(opt: Option[A]): View | |
object OptionMapViewProvider: | |
transparent inline given optionMapViewProvider[A]: OptionMapViewProvider[A] = ${ optionMapViewProviderImpl[A] } | |
def optionMapViewProviderImpl[A : Type](using quotes: Quotes): Expr[OptionMapViewProvider[A]] = | |
import quotes.reflect.* | |
val refinements = optionViewRefinements { tp => | |
tp.asType match | |
case '[t] => Some(TypeRepr.of[Option[t]]) | |
} | |
val refinedType = refineType(TypeRepr.of[OptionMapView[A]], refinements).asType | |
refinedType match | |
case '[t] => | |
'{ new OptionMapViewProvider[A] { | |
override type View = t | |
override def view(opt: Option[A]): View = new OptionMapView(opt).asInstanceOf[t] | |
}.asInstanceOf[OptionMapViewProvider[A] { type View = t }] } | |
extension [A](opt: Option[A])(using p: OptionMapViewProvider[A]) | |
def ? : p.View = p.view(opt) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment