From c436beeed0176780c9c479868267acb767ca4c58 Mon Sep 17 00:00:00 2001 From: lmlynik Date: Sun, 18 Sep 2022 13:35:57 +0200 Subject: [PATCH 1/2] correct valueOrError to valueOrAbort correct report.error to report.errorAndAbort --- _overviews/scala3-macros/faq.md | 2 +- _overviews/scala3-macros/tutorial/macros.md | 16 +++++++++------- _overviews/scala3-macros/tutorial/quotes.md | 2 +- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/_overviews/scala3-macros/faq.md b/_overviews/scala3-macros/faq.md index fc8ccb8972..7a809cdd60 100644 --- a/_overviews/scala3-macros/faq.md +++ b/_overviews/scala3-macros/faq.md @@ -13,7 +13,7 @@ All quotes containing a value of a primitive type is optimised to an `Expr.apply Choose one in your project and stick with a single notation to avoid confusion. ## How do I get a value out of an `Expr`? -If the expression represents a value, you can use `.value`, `.valueOrError` or `Expr.unapply` +If the expression represents a value, you can use `.value`, `.valueOrAbort` or `Expr.unapply` ## How can I get the precise type of an `Expr`? We can get the precise type (`Type`) of an `Expr` using the following pattern match: diff --git a/_overviews/scala3-macros/tutorial/macros.md b/_overviews/scala3-macros/tutorial/macros.md index 81cbc920d1..7a1f6d18af 100644 --- a/_overviews/scala3-macros/tutorial/macros.md +++ b/_overviews/scala3-macros/tutorial/macros.md @@ -113,7 +113,7 @@ def powerCode( x: Expr[Double], n: Expr[Int] )(using Quotes): Expr[Double] = - val value: Double = pow(x.valueOrError, n.valueOrError) + val value: Double = pow(x.valueOrAbort, n.valueOrAbort) Expr(value) ``` Here, the `pow` operation is a simple Scala function that computes the value of `xⁿ`. @@ -131,28 +131,30 @@ Other types can also work if a `ToExpr` is implemented for it, we will [see this ### Extracting Values from Expressions -The second method we use in the implementation of `powerCode` is `Expr[T].valueOrError`, which has an effect opposite to `Expr.apply`. +The second method we use in the implementation of `powerCode` is `Expr[T].valueOrAbort`, which has an effect opposite to `Expr.apply`. It attempts to extract a value of type `T` from an expression of type `Expr[T]`. This can only succeed, if the expression directly contains the code of a value, otherwise, it will throw an exception that stops the macro expansion and reports that the expression did not correspond to a value. -Instead of `valueOrError`, we could also use the `value` operation, which will return an `Option`. +Instead of `valueOrAbort`, we could also use the `value` operation, which will return an `Option`. This way we can report the error with a custom error message. ```scala ... + import quotes.reflect.* (x.value, n.value) match case (Some(base), Some(exponent)) => pow(base, exponent) case (Some(_), _) => - report.error("Expected a known value for the exponent, but was " + n.show, n) + report.errorAndAbort("Expected a known value for the exponent, but was " + n.show, n) case _ => - report.error("Expected a known value for the base, but was " + x.show, x) + report.errorAndAbort("Expected a known value for the base, but was " + x.show, x) ``` Alternatively, we can also use the `Expr.unapply` extractor ```scala ... + import quotes.reflect.* (x, n) match case (Expr(base), Expr(exponent)) => pow(base, exponent) @@ -196,7 +198,7 @@ inline def sumNow(inline nums: Int*): Int = def sumCode(nums: Expr[Seq[Int]])(using Quotes): Expr[Int] = nums match case Varargs(numberExprs) => // numberExprs: Seq[Expr[Int]] - val numbers: Seq[Int] = numberExprs.map(_.valueOrError) + val numbers: Seq[Int] = numberExprs.map(_.valueOrAbort) Expr(numbers.sum) case _ => report.error( "Expected explicit argument" + @@ -244,7 +246,7 @@ inline def test(inline ignore: Boolean, computation: => Unit): Boolean = ${ testCode('ignore, 'computation) } def testCode(ignore: Expr[Boolean], computation: Expr[Unit])(using Quotes) = - if ignore.valueOrError then Expr(false) + if ignore.valueOrAbort then Expr(false) else Expr.block(List(computation), Expr(true)) ``` diff --git a/_overviews/scala3-macros/tutorial/quotes.md b/_overviews/scala3-macros/tutorial/quotes.md index 54ea43d438..7609c9ea36 100644 --- a/_overviews/scala3-macros/tutorial/quotes.md +++ b/_overviews/scala3-macros/tutorial/quotes.md @@ -367,7 +367,7 @@ As with expression quote patterns, type variables are represented using lower ca ## FromExpr -The `Expr.value`, `Expr.valueOrError`, and `Expr.unapply` methods uses intances of `FromExpr` to extract the value if possible. +The `Expr.value`, `Expr.valueOrAbort`, and `Expr.unapply` methods uses intances of `FromExpr` to extract the value if possible. ```scala extension [T](expr: Expr[T]): def value(using Quotes)(using fromExpr: FromExpr[T]): Option[T] = From 85b5f31913b8ae3cb7385ac882ed20433910c2e1 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 5 Oct 2022 14:55:52 +0200 Subject: [PATCH 2/2] clear up how report is imported --- _overviews/scala3-macros/tutorial/macros.md | 49 ++++++++++++++------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/_overviews/scala3-macros/tutorial/macros.md b/_overviews/scala3-macros/tutorial/macros.md index 7a1f6d18af..5074da58bc 100644 --- a/_overviews/scala3-macros/tutorial/macros.md +++ b/_overviews/scala3-macros/tutorial/macros.md @@ -14,13 +14,16 @@ Macros enable us to do exactly this: treat **programs as data** and manipulate t ## Macros Treat Programs as Values With a macro, we can treat programs as values, which allows us to analyze and generate them at compile time. + A Scala expression with type `T` is represented by an instance of the type `scala.quoted.Expr[T]`. We will dig into the details of the type `Expr[T]`, as well as the different ways of analyzing and constructing instances, when talking about [Quoted Code][quotes] and [Reflection][tasty]. For now, it suffices to know that macros are metaprograms that manipulate expressions of type `Expr[T]`. -The following macro implementation simply prints the expression of the provided argument: +The following macro implementation prints the expression of the provided argument: ```scala +import scala.quoted.* // imports Quotes, Expr + def inspectCode(x: Expr[Any])(using Quotes): Expr[Any] = println(x.show) x @@ -138,12 +141,25 @@ This can only succeed, if the expression directly contains the code of a value, Instead of `valueOrAbort`, we could also use the `value` operation, which will return an `Option`. This way we can report the error with a custom error message. +#### Reporting Custom Error Messages + +The contextual `Quotes` parameter provides a `report` object that we can use to report a custom error message. +Within a macro implementation method, you can access the contextual `Quotes` parameter with the `quotes` method +(imported with `import scala.quoted.*`), then import the `report` object by `import quotes.reflect.report`. + +#### Providing the Custom Error + +We will provide the custom error message by calling `errorAndAbort` on the `report` object as follows: ```scala - ... - import quotes.reflect.* +def powerCode( + x: Expr[Double], + n: Expr[Int] +)(using Quotes): Expr[Double] = + import quotes.reflect.report (x.value, n.value) match case (Some(base), Some(exponent)) => - pow(base, exponent) + val value: Double = pow(base, exponent) + Expr(value) case (Some(_), _) => report.errorAndAbort("Expected a known value for the exponent, but was " + n.show, n) case _ => @@ -154,14 +170,14 @@ Alternatively, we can also use the `Expr.unapply` extractor ```scala ... - import quotes.reflect.* (x, n) match case (Expr(base), Expr(exponent)) => - pow(base, exponent) + val value: Double = pow(base, exponent) + Expr(value) case (Expr(_), _) => ... case _ => ... ``` -The operations `value`, `valueOrError`, and `Expr.unapply` will work for all _primitive types_, _tuples_ of any arity, `Option`, `Seq`, `Set`, `Map`, `Either` and `StringContext`. +The operations `value`, `valueOrAbort`, and `Expr.unapply` will work for all _primitive types_, _tuples_ of any arity, `Option`, `Seq`, `Set`, `Map`, `Either` and `StringContext`. Other types can also work if an `FromExpr` is implemented for it, we will [see this later][quotes]. @@ -169,15 +185,17 @@ Other types can also work if an `FromExpr` is implemented for it, we will [see t In the implementation of `inspectCode`, we have already seen how to convert expressions to the string representation of their _source code_ using the `.show` method. This can be useful to perform debugging on macro implementations: + + ```scala def debugPowerCode( x: Expr[Double], n: Expr[Int] )(using Quotes): Expr[Double] = println( - s"""powerCode - | x := ${x.show} - | n := ${n.show}""".stripMargin) + s"powerCode \n" + + s" x := ${x.show}\n" + + s" n := ${n.show}") val code = powerCode(x, n) println(s" code := ${code.show}") code @@ -190,23 +208,24 @@ Varargs in Scala are represented with `Seq`, hence when we write a macro with a It is possible to recover each individual argument (of type `Expr[T]`) using the `scala.quoted.Varargs` extractor. ```scala -import scala.quoted.Varargs +import scala.quoted.* // imports `Varargs`, `Quotes`, etc. inline def sumNow(inline nums: Int*): Int = ${ sumCode('nums) } def sumCode(nums: Expr[Seq[Int]])(using Quotes): Expr[Int] = + import quotes.reflect.report nums match case Varargs(numberExprs) => // numberExprs: Seq[Expr[Int]] val numbers: Seq[Int] = numberExprs.map(_.valueOrAbort) Expr(numbers.sum) - case _ => report.error( - "Expected explicit argument" + - "Notation `args: _*` is not supported.", numbersExpr) + case _ => report.errorAndAbort( + "Expected explicit varargs sequence. " + + "Notation `args*` is not supported.", nums) ``` The extractor will match a call to `sumNow(1, 2, 3)` and extract a `Seq[Expr[Int]]` containing the code of each parameter. -But, if we try to match the argument of the call `sumNow(nums: _*)`, the extractor will not match. +But, if we try to match the argument of the call `sumNow(nums*)`, the extractor will not match. `Varargs` can also be used as a constructor. `Varargs(Expr(1), Expr(2), Expr(3))` will return an `Expr[Seq[Int]]`. We will see how this can be useful later.