|
| 1 | +import scala.annotation.experimental |
| 2 | +import scala.quoted.* |
| 3 | +import scala.annotation.tailrec |
| 4 | + |
| 5 | +object FlatMap { |
| 6 | + @experimental inline def derived[F[_]]: FlatMap[F] = MacroFlatMap.derive |
| 7 | +} |
| 8 | +trait FlatMap[F[_]]{ |
| 9 | + def tailRecM[A, B](a: A)(f: A => F[Either[A, B]]): F[B] |
| 10 | +} |
| 11 | + |
| 12 | +@experimental |
| 13 | +object MacroFlatMap: |
| 14 | + |
| 15 | + inline def derive[F[_]]: FlatMap[F] = ${ flatMap } |
| 16 | + |
| 17 | + def flatMap[F[_]: Type](using Quotes): Expr[FlatMap[F]] = '{ |
| 18 | + new FlatMap[F]: |
| 19 | + def tailRecM[A, B](a: A)(f: A => F[Either[A, B]]): F[B] = |
| 20 | + ${ deriveTailRecM('{ a }, '{ f }) } |
| 21 | + } |
| 22 | + |
| 23 | + def deriveTailRecM[F[_]: Type, A: Type, B: Type]( |
| 24 | + a: Expr[A], |
| 25 | + f: Expr[A => F[Either[A, B]]] |
| 26 | + )(using q: Quotes): Expr[F[B]] = |
| 27 | + import quotes.reflect.* |
| 28 | + |
| 29 | + val body: PartialFunction[(Symbol, TypeRepr), Term] = { |
| 30 | + case (method, tpe) => { |
| 31 | + given q2: Quotes = method.asQuotes |
| 32 | + '{ |
| 33 | + def step(x: A): B = ??? |
| 34 | + ??? |
| 35 | + }.asTerm |
| 36 | + } |
| 37 | + } |
| 38 | + |
| 39 | + val term = '{ $f($a) }.asTerm |
| 40 | + val name = Symbol.freshName("$anon") |
| 41 | + val parents = List(TypeTree.of[Object], TypeTree.of[F[B]]) |
| 42 | + |
| 43 | + extension (sym: Symbol) def overridableMembers: List[Symbol] = |
| 44 | + val member1 = sym.methodMember("abstractEffect")(0) |
| 45 | + val member2 = sym.methodMember("concreteEffect")(0) |
| 46 | + def meth(member: Symbol) = Symbol.newMethod(sym, member.name, This(sym).tpe.memberType(member), Flags.Override, Symbol.noSymbol) |
| 47 | + List(meth(member1), meth(member2)) |
| 48 | + |
| 49 | + val cls = Symbol.newClass(Symbol.spliceOwner, name, parents.map(_.tpe), _.overridableMembers, None) |
| 50 | + |
| 51 | + def transformDef(method: DefDef)(argss: List[List[Tree]]): Option[Term] = |
| 52 | + val sym = method.symbol |
| 53 | + Some(body.apply((sym, method.returnTpt.tpe))) |
| 54 | + |
| 55 | + val members = cls.declarations |
| 56 | + .filterNot(_.isClassConstructor) |
| 57 | + .map: sym => |
| 58 | + sym.tree match |
| 59 | + case method: DefDef => DefDef(sym, transformDef(method)) |
| 60 | + case _ => report.errorAndAbort(s"Not supported: $sym in ${sym.owner}") |
| 61 | + |
| 62 | + val newCls = New(TypeIdent(cls)).select(cls.primaryConstructor).appliedToNone |
| 63 | + Block(ClassDef(cls, parents, members) :: Nil, newCls).asExprOf[F[B]] |
0 commit comments