Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.thoughtworks.dsl.reset.scala Maven / Gradle / Ivy
package com.thoughtworks
package dsl
import keywords._
import com.thoughtworks.dsl.keywords._, Match._
import scala.quoted.Quotes
import collection.immutable.Queue
import scala.util.control.Exception.Catcher
/**
* @example
* Suppose you are generating a random integer less than 100, whose first digit and second digit is different. A
* solution is generating integers in an infinite loop, and [[Return]] from the loop when the generated integer
* conforms with requirements.
*
* {{{
* import scala.util.Random
* import scala.util.control.TailCalls
* import scala.util.control.TailCalls.TailRec
* import com.thoughtworks.dsl.reset
* def randomInt(): TailRec[Int] = reset {
* while (true) {
* val r = Random.nextInt(100)
* if (r % 10 != r / 10) {
* !Return(TailCalls.done(r))
* }
* }
* throw new AssertionError("Unreachable code");
* }
*
* val r = randomInt().result
* r should be < 100
* r % 10 should not be r / 10
* }}}
*
* @example
* Since the [[Return]] keyword can automatically lift the return type, `TailCalls.done` can be omitted.
*
* {{{
* import scala.util.Random
* import scala.util.control.TailCalls
* import scala.util.control.TailCalls.TailRec
* def randomInt(): TailRec[Int] = reset {
* while (true) {
* val r = Random.nextInt(100)
* if (r % 10 != r / 10) {
* !Return(r)
* }
* }
* throw new AssertionError("Unreachable code");
* }
*
* val r = randomInt().result
* r should be < 100
* r % 10 should not be r / 10
* }}}
*/
object reset {
private class Macros[Q <: Quotes](resetDescendant: Boolean)(using val qctx: Q) {
import qctx.reflect.{_, given}
def reify[V](body: quoted.Expr[_])(using valueType: quoted.Type[V]): quoted.Expr[_] = {
val bodyTerm = body.asTerm.underlyingArgument
val reifiedTerm = KeywordTree(bodyTerm).keywordTerm
reifiedTerm.usingExpr { [K] => (k: quoted.Expr[K]) => (tk: quoted.Type[K]) ?=>
'{keywords.Typed[K, V]($k)}: quoted.Expr[_]
}
}
def resetDefDef(defDef: DefDef): DefDef = {
val DefDef(name, typeParamsAndParams, tpt, rhsOption) = defDef
rhsOption match {
case Some(rhs) if resetDescendant =>
rhs match {
case matchTree @ qctx.reflect.Match(scrutinee, cases) =>
DefDef.copy(defDef)(
name, typeParamsAndParams, tpt, Some(
qctx.reflect.Match.copy(matchTree)(
scrutinee,
cases.map {
case caseDef @ CaseDef(pattern, guard, caseRhs) =>
CaseDef.copy(caseDef)(pattern, guard, resetTerm(caseRhs))
}
)
)
)
case _ =>
DefDef.copy(defDef)(
name, typeParamsAndParams, tpt, Some(resetTerm(rhs))
)
}
case _ =>
defDef
}
}
def resetTerm(term: Term): Term = {
term.usingExpr { [Value] => (body: quoted.Expr[Value]) => (tv: quoted.Type[Value]) ?=>
// given quoted.Type[Value] = tv
reset[Value, Value](body).asTerm
}
}
def reset[Value, Domain](body: quoted.Expr[Value])(using valueType: quoted.Type[Value], domainType: quoted.Type[Domain]): quoted.Expr[Domain] = {
KeywordTree(body.asTerm.underlyingArgument) match {
case Pure(pure, _) if TypeRepr.of[Value] <:< TypeRepr.of[Domain] =>
pure.asExprOf[Domain]
case keywordTree =>
val reifiedTerm = keywordTree.keywordTerm
reifiedTerm.usingExpr { [K] => (k: quoted.Expr[K]) => ( keywordType: quoted.Type[K]) ?=>
{
Implicits.search(TypeRepr.of[Dsl.Run[K, Domain, Value]]) match {
case success: ImplicitSearchSuccess =>
'{
${success.tree.asExprOf[Dsl.Run[K, Domain, Value]]}.apply($k)
}
case failure: ImplicitSearchFailure =>
report.error(s"The keyword ${quoted.Type.show[K]} is not supported in a `reset` block that returns ${quoted.Type.show[Domain]}\n${failure.explanation}", body.asTerm.underlyingArgument.pos)
body.asTerm.asExprOf[Domain]
}
}
}
}
}
extension (tpe: TypeRepr)
def usingType[B, Bound](f: [A <: Bound] => (ta : quoted.Type[A]) => B): B = {
f(/*given*/ tpe.asType.asInstanceOf)
}
extension (term: Term)
def usingExpr[B, UpperBound](f: [A <: UpperBound] => quoted.Expr[A] => (ta: quoted.Type[A]) ?=> B): B = {
def helper[A <: UpperBound] = {
implicit val tpe: quoted.Type[A] = term.tpe.asType.asInstanceOf[quoted.Type[A]]
f[A](term.asExprOf[A])(using tpe)
}
helper
}
val List(shiftSymbol) = Symbol.requiredModule("com.thoughtworks.dsl.Dsl").declaredMethod("shift")
sealed trait KeywordTree {
def keywordTerm: Term
def valueType: TypeRepr
def where(blockTemplate: qctx.reflect.Block, statement: Statement): KeywordTree = Let(blockTemplate, statement, this)
def block: KeywordTree = Block(this)
def usingKeyword[B, Bound](f: [K, V <: Bound] => quoted.Expr[K] => (tk: quoted.Type[K], tv: quoted.Type[V]) => B): B = {
keywordTerm.usingExpr[B, Any]{ [K] => (keyword: quoted.Expr[K]) => (keywordType: quoted.Type[K]) ?=>
valueType.usingType[B, Bound]{ [V <: Bound] => (valueType: quoted.Type[V]) =>
given quoted.Type[V] = valueType
f(keyword)(/*given*/ keywordType, valueType)
}
}
}
def flatMap(flatMapper: Term => KeywordTree): KeywordTree = {
FlatMap(this, flatMapper)
}
}
object KeywordTree {
def apply(term: Term): KeywordTree = {
term match {
case
Apply(
TypeApply(
shiftMethod,
List(_, valueTypeTree)
),
List(
from
)
)
if shiftMethod.symbol == shiftSymbol =>
KeywordTree(from).flatMap { pureFrom =>
Keyword(pureFrom, valueTypeTree.tpe)
}
case typeApply @ TypeApply(fun, args) =>
KeywordTree(fun).flatMap { pureFun =>
Pure(TypeApply.copy(typeApply)(pureFun, args), term.tpe)
}
case Apply(fun, args) =>
KeywordTree(fun).flatMap { pureFun =>
def loop(args: List[Term], pureArgs: Queue[Term]): KeywordTree = {
args match {
case Nil =>
Pure(Apply.copy(fun)(pureFun, pureArgs.toList), term.tpe)
case head :: tail =>
KeywordTree(head).flatMap { pureHead =>
loop(tail, pureArgs :+ pureHead)
}
}
}
loop(args, Queue.empty)
}
case repeated @ Repeated(elems, elemtpt) =>
def loop(args: List[Term], pureElems: Queue[Term]): KeywordTree = {
args match {
case Nil =>
Pure(Repeated(pureElems.toList, elemtpt), term.tpe)
case head :: tail =>
KeywordTree(head).flatMap { pureHead =>
loop(tail, pureElems :+ pureHead)
}
}
}
loop(elems, Queue.empty)
case select @ Select(qualifier, name) =>
KeywordTree(qualifier).flatMap { pureQualifier =>
Pure(Select.copy(select)(pureQualifier, name), term.tpe)
}
case selectOuter @ SelectOuter(qualifier, levels, tpe) =>
KeywordTree(qualifier).flatMap { pureQualifier =>
Pure(SelectOuter.copy(selectOuter)(pureQualifier, levels, tpe), term.tpe)
}
case (_: Literal) | (_: Ident) | (_: New) | (_: This) | (_: Super) | (_: Closure) =>
Pure(term, term.tpe)
case block @ qctx.reflect.Block(
List(
defDef: DefDef
),
closure @ Closure(ident: Ident, _)
) if (ident.name == defDef.name) =>
Pure(qctx.reflect.Block.copy(block)(
List(resetDefDef(defDef)),
closure,
), term.tpe)
case block @ qctx.reflect.Block(stats, expr) =>
def loop(stats: List[Statement] = stats): KeywordTree = {
stats match {
case Nil =>
KeywordTree(expr)
case head :: tail =>
head match {
case headTerm: Term =>
KeywordTree(headTerm).flatMap(loop(tail).where(block, _))
case headVal @ ValDef(name, tpt, Some(rhs)) =>
KeywordTree(rhs).flatMap { pureRhs =>
loop(tail).where(block, ValDef.copy(headVal)(name, tpt, Some(pureRhs)))
}
case defDef: DefDef =>
loop(tail).where(block, resetDefDef(defDef))
case _ =>
loop(tail).where(block, head)
}
}
}
loop().block
case qctx.reflect.If(cond, thenp, elsep) =>
If(
KeywordTree(cond),
Suspend(KeywordTree(thenp)),
Suspend(KeywordTree(elsep)),
term.tpe
)
case qctx.reflect.Match(upstream, cases) =>
Match(
KeywordTree(upstream),
cases.map {
case caseDef @ CaseDef(pattern, guard, body) =>
caseDef -> KeywordTree(body)
},
term.tpe
)
case Try(expr, cases, None) =>
TryCatch(
Suspend(KeywordTree(expr)),
cases.map {
case caseDef @ CaseDef(pattern, guard, body) =>
caseDef -> KeywordTree(body)
},
term.tpe
)
case Try(expr, Nil, Some(finalizer)) =>
TryFinally(
Suspend(KeywordTree(expr)),
Suspend(KeywordTree(finalizer)),
term.tpe
)
case Try(expr, cases, Some(finalizer)) =>
TryCatchFinally(
Suspend(KeywordTree(expr)),
cases.map {
case caseDef @ CaseDef(pattern, guard, body) =>
caseDef -> KeywordTree(body)
},
Suspend(KeywordTree(finalizer)),
term.tpe
)
case typed @ qctx.reflect.Typed(expr, tpt) =>
KeywordTree(expr).flatMap { pureExpr =>
Pure(qctx.reflect.Typed.copy(typed)(pureExpr, tpt), term.tpe)
}
case assign @ Assign(lhs, rhs) =>
KeywordTree(lhs).flatMap { pureLhs =>
KeywordTree(rhs).flatMap { pureRhs =>
Pure(Assign.copy(assign)(pureLhs, pureRhs), term.tpe)
}
}
case Inlined(_, bindings, term) =>
KeywordTree(qctx.reflect.Block(bindings, term))
case whileTerm @ qctx.reflect.While(cond, body) =>
While(Suspend(KeywordTree(cond)), Suspend(KeywordTree(body)))
// case returnTree @ qctx.reflect.Return(expr, _from) =>
// KeywordTree(expr).flatMap { pureExpr =>
// Return(pureExpr, expr.tpe)
// }
case returnTree @ qctx.reflect.Return(expr, from) =>
KeywordTree(expr).flatMap { pureExpr =>
Pure(qctx.reflect.Return.copy(returnTree)(pureExpr, from), term.tpe)
}
}
}
}
private def usingCases[R, A](
cases: Seq[(CaseDef, KeywordTree)], caseValueType: TypeRepr
)(
f: [Set, Value] => quoted.Expr[PartialFunction[A, Set]] => (ts: quoted.Type[Set], tv: quoted.Type[Value]) => R
)(
using quoted.Type[A]
): R = {
// val keywordTerms = cases.map { (caseDef, keywordTree) =>
// caseDef -> keywordTree.keywordTerm
// }
val caseSetType = cases.view.zipWithIndex.foldRight(TypeRepr.of[Nothing]) {
case (((caseDef, keywordTree), i), accumulator) =>
val indexType = quoted.Expr(i).asTerm.tpe
val AppliedType(withIndex, _) = TypeRepr.of[WithIndex[_, _]]
val AppliedType(ccons, _) = TypeRepr.of[Any +: Nothing]
ccons.appliedTo(
List(
withIndex.appliedTo(
List(indexType, keywordTree.keywordTerm.tpe)
),
accumulator
)
)
}
caseValueType.usingType { [Value] => ( valueTpe: quoted.Type[Value]) =>
given quoted.Type[Value] = valueTpe
caseSetType.usingType { [Set] => (setTpe : quoted.Type[Set]) =>
given quoted.Type[Set] = setTpe
'{
{
case e: Throwable if false => ???
}: PartialFunction[A, Set]
}.asTerm.underlyingArgument match {
case qctx.reflect.Typed(
pfBlock @ qctx.reflect.Block(
List(
defDef @ DefDef(name, typeParamsAndParams, returnTpt, Some(
matchTree @ qctx.reflect.Match(
scrutinee,
List(caseDefTemplate)
)
))
),
closure
),
_
) =>
val newCases = cases.view.zipWithIndex.map {
case ((caseDef, caseKeywordTree), i) =>
caseKeywordTree.usingKeyword {
[BodyKeyword, BodyValue] =>
(bodyExpr: quoted.Expr[BodyKeyword]) =>
(bodyKeywordTpe: quoted.Type[BodyKeyword], bodyValueTpe: quoted.Type[BodyValue]) =>
given quoted.Type[BodyKeyword] = bodyKeywordTpe
given quoted.Type[BodyValue] = bodyValueTpe
quoted.Expr(i).asTerm.usingExpr[CaseDef, Int] { [Index <: Int] => (indexExpr: quoted.Expr[Index]) => (indexType: quoted.Type[Index]) ?=>
CaseDef.copy(caseDef)(
caseDef.pattern,
caseDef.guard,
'{WithIndex[Index, BodyKeyword]($indexExpr, $bodyExpr).asInstanceOf[Set]}.asTerm
).changeOwner(defDef.symbol)
}
}
}.toList
val pfTerm = qctx.reflect.Block.copy(pfBlock)(List(DefDef.copy(defDef)(name, typeParamsAndParams, returnTpt, Some(qctx.reflect.Match.copy(matchTree)(scrutinee, newCases)))), closure)
f[Set, Value](pfTerm.asExprOf[PartialFunction[A, Set]])(setTpe, valueTpe)
}
}
}
}
case class If(cond: KeywordTree, thenp: KeywordTree, elsep: KeywordTree, valueType: TypeRepr) extends KeywordTree {
lazy val keywordTerm = cond.usingKeyword[Term, Boolean] {
[CondKeyword, CondValue <: Boolean] =>
(condExpr: quoted.Expr[CondKeyword]) =>
( condKeywordType: quoted.Type[CondKeyword], condValueType: quoted.Type[CondValue]) =>
given quoted.Type[CondKeyword] = condKeywordType
given quoted.Type[CondValue] = condValueType
thenp.usingKeyword[Term, Boolean] {
[ThenpKeyword, ThenpValue <: Boolean] =>
(thenpExpr: quoted.Expr[ThenpKeyword]) =>
(thenpKeywordType: quoted.Type[ThenpKeyword], thenpValueType: quoted.Type[ThenpValue]) =>
given quoted.Type[ThenpKeyword] = thenpKeywordType
given quoted.Type[ThenpValue] = thenpValueType
elsep.usingKeyword[Term, Boolean] {
[ElsepKeyword, ElsepValue <: Boolean] =>
(elsepExpr: quoted.Expr[ElsepKeyword]) =>
(elsepKeywordType: quoted.Type[ElsepKeyword], elsepValueType: quoted.Type[ElsepValue]) =>
given quoted.Type[ElsepKeyword] = elsepKeywordType
given quoted.Type[ElsepValue] = elsepValueType
valueType.usingType { [Result] => (resultType: quoted.Type[Result]) =>
given quoted.Type[Result] = resultType
'{
com.thoughtworks.dsl.keywords.If(
$condExpr,
$thenpExpr,
$elsepExpr,
)
}.asTerm
}
}
}
}
}
case class Match(expr: KeywordTree, cases: Seq[(CaseDef, KeywordTree)], valueType: TypeRepr) extends KeywordTree {
lazy val keywordTerm = {
expr.usingKeyword { [K, V] =>
(exprKeywordExpr: quoted.Expr[K]) =>
(exprKeywordType: quoted.Type[K], exprValueType: quoted.Type[V]) =>
given quoted.Type[K] = exprKeywordType
given quoted.Type[V] = exprValueType
usingCases[Term, V](cases, valueType) {
[CaseSet, CaseValue] =>
(pf: quoted.Expr[PartialFunction[V, CaseSet]]) =>
(caseSetType: quoted.Type[CaseSet], caseValueType: quoted.Type[CaseValue]) =>
given quoted.Type[CaseSet] = caseSetType
given quoted.Type[CaseValue] = caseValueType
'{
com.thoughtworks.dsl.keywords.Match(
$exprKeywordExpr,
$pf
)
}.asTerm
}
}
}
}
case class TryCatchFinally(expr: KeywordTree, cases: Seq[(CaseDef, KeywordTree)], finalizer: KeywordTree, valueType: TypeRepr) extends KeywordTree {
lazy val keywordTerm = {
usingCases[Term, Throwable](cases, valueType) {
[Set, Value] =>
(pf: quoted.Expr[PartialFunction[Throwable, Set]]) =>
(setType: quoted.Type[Set], valueType: quoted.Type[Value]) =>
given quoted.Type[Set] = setType
given quoted.Type[Value] = valueType
expr.usingKeyword[Term, Value] { [K, V <: Value] =>
(tryKeywordExpr: quoted.Expr[K]) =>
(tryKeywordTpe: quoted.Type[K], tryValueTpe: quoted.Type[V]) =>
given quoted.Type[K] = tryKeywordTpe
given quoted.Type[V] = tryValueTpe
finalizer.usingKeyword {
[FinalizerKeyword, FinalizerValue] =>
(finalizerKeywordExpr: quoted.Expr[FinalizerKeyword]) =>
(finalizerKeywordType: quoted.Type[FinalizerKeyword], finalizerValueType: quoted.Type[FinalizerValue]) =>
given quoted.Type[FinalizerKeyword] = finalizerKeywordType
given quoted.Type[FinalizerValue] = finalizerValueType
'{
com.thoughtworks.dsl.keywords.TryCatchFinally(
$tryKeywordExpr,
$pf,
$finalizerKeywordExpr
)
}.asTerm
}
}
}
}
}
case class TryFinally(expr: KeywordTree, finalizer: KeywordTree, valueType: TypeRepr) extends KeywordTree {
lazy val keywordTerm = {
valueType.usingType { [Value] => (tv: quoted.Type[Value]) =>
given quoted.Type[Value] = tv
expr.usingKeyword[Term, Value] {
[K, V <: Value] =>
(tryKeywordExpr: quoted.Expr[K]) =>
(tryKeywordType: quoted.Type[K], tryValueType: quoted.Type[V]) =>
given quoted.Type[K] = tryKeywordType
given quoted.Type[V] = tryValueType
finalizer.usingKeyword {
[FinalizerKeyword, FinalizerValue] =>
(finalizerKeywordExpr: quoted.Expr[FinalizerKeyword]) =>
(finalizerKeywordType: quoted.Type[FinalizerKeyword], finalizerValueType: quoted.Type[FinalizerValue]) =>
given quoted.Type[FinalizerKeyword] = finalizerKeywordType
given quoted.Type[FinalizerValue] = finalizerValueType
'{
com.thoughtworks.dsl.keywords.TryFinally(
$tryKeywordExpr,
$finalizerKeywordExpr
)
}.asTerm
}
}
}
}
}
case class TryCatch(expr: KeywordTree, cases: Seq[(CaseDef, KeywordTree)], valueType: TypeRepr) extends KeywordTree {
lazy val keywordTerm = {
usingCases[Term, Throwable](cases, valueType) {
[Set, Value] =>
(pf: quoted.Expr[PartialFunction[Throwable, Set]]) =>
(setType: quoted.Type[Set], vt: quoted.Type[Value]) =>
given quoted.Type[Set] = setType
given quoted.Type[Value] = vt
expr.usingKeyword[Term, Value] { [K, V <: Value] =>
(tryKeywordExpr: quoted.Expr[K]) =>
(tryKeywordTpe: quoted.Type[K], tryValueTpe: quoted.Type[V]) =>
given quoted.Type[K] = tryKeywordTpe
given quoted.Type[V] = tryValueTpe
'{
com.thoughtworks.dsl.keywords.TryCatch[K, Set](
$tryKeywordExpr,
$pf
)
}.asTerm
}
}
}
}
case class Suspend(body: KeywordTree) extends KeywordTree {
export body.valueType
lazy val keywordTerm = body.usingKeyword {
[BodyKeyword, BodyValue] =>
(bodyExpr: quoted.Expr[BodyKeyword]) =>
(bodyKeywordTpe: quoted.Type[BodyKeyword], bodyValueTpe: quoted.Type[BodyValue]) =>
given quoted.Type[BodyKeyword] = bodyKeywordTpe
given quoted.Type[BodyValue] = bodyValueTpe
'{
com.thoughtworks.dsl.keywords.Suspend(() => $bodyExpr)
}.asTerm
}
}
case class While(cond: KeywordTree, body: KeywordTree) extends KeywordTree {
def valueType = TypeRepr.of[Unit]
lazy val keywordTerm = {
cond.usingKeyword{
[CondKeyword, CondValue] =>
(condExpr: quoted.Expr[CondKeyword]) =>
(condKeywordTpe: quoted.Type[CondKeyword], condValueTpe: quoted.Type[CondValue]) =>
given quoted.Type[CondKeyword] = condKeywordTpe
given quoted.Type[CondValue] = condValueTpe
body.usingKeyword {
[BodyKeyword, BodyValue] =>
(bodyExpr: quoted.Expr[BodyKeyword]) =>
(bodyKeywordTpe: quoted.Type[BodyKeyword], bodyValueTpe: quoted.Type[BodyValue]) =>
given quoted.Type[BodyKeyword] = bodyKeywordTpe
given quoted.Type[BodyValue] = bodyValueTpe
'{
com.thoughtworks.dsl.keywords.While($condExpr, $bodyExpr)
}.asTerm
}
}
}
}
case class Return(returnValue: Term, returnValueType: TypeRepr) extends KeywordTree {
def valueType = TypeRepr.of[Nothing]
lazy val keywordTerm = {
returnValueType.usingType { [A0] => (tpe0: quoted.Type[A0]) =>
given quoted.Type[A0] = tpe0
returnValue.usingExpr[Term, A0] { [A <: A0] => (expr: quoted.Expr[A]) => (tpe: quoted.Type[A]) ?=>
'{
com.thoughtworks.dsl.keywords.Return[A0]($expr)
}.asTerm
}
}
}
}
case class Block(body: KeywordTree) extends KeywordTree {
def valueType = body.valueType
lazy val keywordTerm = {
body.keywordTerm
}
}
case class Let(blockTemplate: qctx.reflect.Block, stat: Statement, rest: KeywordTree) extends KeywordTree {
export rest.valueType
lazy val keywordTerm = {
@annotation.tailrec
def loop(stats: Queue[Statement], rest: KeywordTree): Term = {
rest match {
case Let(`blockTemplate`, head, tail) =>
loop(stats :+ head, tail)
case otherKeyword =>
qctx.reflect.Block.copy(blockTemplate)(stats.toList, otherKeyword.keywordTerm)
}
}
loop(Queue(stat), rest)
}
override def flatMap(flatMapper: Term => KeywordTree): KeywordTree = {
copy(rest = rest.flatMap(flatMapper))
}
}
case class FlatMap(upstream: KeywordTree, flatMapper: Term => KeywordTree) extends KeywordTree {
lazy val (keywordTerm, valueType) = {
upstream.usingKeyword {
[K, V] =>
(keywordExpr: quoted.Expr[K]) =>
(keywordTpe: quoted.Type[K], valueTpe: quoted.Type[V]) =>
{
given quoted.Type[K] = keywordTpe
given quoted.Type[V] = valueTpe
def go[X] = {
var innerKeywordTreeOption: Option[KeywordTree] = None
val anyFunction = '{ (dslValue: V) =>
${
val innerKeywordTree = flatMapper('dslValue.asTerm)
innerKeywordTreeOption = Some(innerKeywordTree)
innerKeywordTree.keywordTerm.changeOwner(Symbol.spliceOwner).asExpr
}
}
val Some(innerKeywordTree) = innerKeywordTreeOption
given quoted.Type[X] = innerKeywordTree.keywordTerm.tpe.asType.asInstanceOf[quoted.Type[X]]
val flatMapperExpr ='{$anyFunction.asInstanceOf[V => X]}
val flatMapperTerm = flatMapperExpr.asTerm
val flatMapObject = '{keywords.FlatMap}.asTerm
Apply(TypeApply(Select.unique(flatMapObject, "apply"), List(
TypeTree.of[K],
TypeTree.of[X],
)), List(keywordExpr.asTerm, flatMapperTerm)) -> innerKeywordTree.valueType
}
go
}
}
}
override def flatMap(nextFlatMapper: Term => KeywordTree): KeywordTree = {
upstream.flatMap { term =>
flatMapper(term).flatMap(nextFlatMapper)
}
}
}
case class Keyword(keywordTerm: Term, valueType: TypeRepr) extends KeywordTree
case class Pure(term: Term, valueType: TypeRepr) extends KeywordTree {
override def flatMap(flatMapper: Term => KeywordTree) = flatMapper(term)
// override def where(blockTemplate: qctx.reflect.Block, statement: Statement) = {
// Pure(qctx.reflect.Block.copy(blockTemplate)(List(statement), term))
// }
override def block = this
def keywordTerm: Term = {
// We don't need widenTermRefByName because Pure is covariant, so it could be widen automatically as needed
valueType/*.widenTermRefByName*/.usingType { [A] => (ta: quoted.Type[A]) =>
given quoted.Type[A] = ta
'{
keywords.Pure[A](${term.asExprOf[A]})
}.asTerm
}
}
}
}
object Macros {
def reify[V](body: quoted.Expr[_])(using qctx: Quotes, tv: quoted.Type[V]): quoted.Expr[_] = {
Macros[qctx.type](resetDescendant = false).reify[V](body/*.underlyingArgument*/)
}
def reset[From, To](body: quoted.Expr[From])(using qctx: Quotes, fromType: quoted.Type[From], toType: quoted.Type[To]): quoted.Expr[To] = {
import qctx.reflect.{_, given}
val result: quoted.Expr[To] = Macros[qctx.type](resetDescendant = false).reset(body/*.underlyingArgument*/)
// report.warning(result.asTerm.show(using qctx.reflect.Printer.TreeStructure))
// report.warning(result.asTerm.show)
result
}
}
transparent inline def reify[Value](inline value: Value): Any = ${
Macros.reify[Value]('value)
}
class *[Functor[_]]() {
inline def apply[Value](inline value: Value): Functor[Value] = ${
Macros.reset[Value, Functor[Value]]('value)
}
}
inline def *[Domain[_]]: *[Domain] = new *[Domain]
inline def apply[Value](inline value: Value): Value = ${
Macros.reset[Value, Value]('value)
}
}