|  | 
|  | 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