Skip to content

Instantly share code, notes, and snippets.

@lbialy
Forked from prolativ/Helpers.scala
Last active February 27, 2024 17:19
Show Gist options
  • Save lbialy/b3c969824f9c6f612c1776da817d0f3a to your computer and use it in GitHub Desktop.
Save lbialy/b3c969824f9c6f612c1776da817d0f3a to your computer and use it in GitHub Desktop.
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
//> 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()
}
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)
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