From 23e24e843a49629977c4b88f330469c1eaf118ca Mon Sep 17 00:00:00 2001 From: Aleksander Rainko Date: Sun, 21 Apr 2024 02:25:01 +0200 Subject: [PATCH 1/2] Add `Matching function expressions` It seems like no one on the internet knows how to do this properly, so after drunkenly stumbling upon the correct syntax I decided to throw this small section together. --- _overviews/scala3-macros/tutorial/quotes.md | 62 ++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/_overviews/scala3-macros/tutorial/quotes.md b/_overviews/scala3-macros/tutorial/quotes.md index be3495895..904e4b859 100644 --- a/_overviews/scala3-macros/tutorial/quotes.md +++ b/_overviews/scala3-macros/tutorial/quotes.md @@ -245,7 +245,67 @@ case ... ### Matching function expressions -*Coming soon* +Let's start with the most straightforward example, matching an identity function expression: + +```scala +def matchIdentityFunction[A: Type](func: Expr[A => A])(using Quotes): Unit = + func match + case '{ (arg: A) => arg } => +``` +The above matches function expressions that just return their arguments, like: + +```scala +(value: Int) => value +``` + +We can also match more complex expressions, like method call chains: + +```scala +def matchMethodCallChain(func: Expr[String => String])(using Quotes) = + func match + case '{ (arg: String) => arg.toLowerCase.strip.trim } => +``` + +But what about the cases where we want more flexibility (eg. we know the subset of methods that will be called but not neccessarily their order)? + +#### Iterative deconstruction of a function expression + +Let's imagine we need a macro that collects names of methods used in an expression of type `FieldName => FieldName`, for a definition of `FieldName` that looks like this: + +```scala +trait FieldName: + def uppercase: FieldName + def lowercase: FieldName +``` + +The implementation itself would look like this: + +```scala +def collectUsedMethods(func: Expr[FieldName => FieldName])(using Quotes): List[String] = + def recurse(current: Expr[FieldName => FieldName], acc: List[String])(using Quotes): List[String] = + current match + // $body is the next tree with the '.lowercase' call stripped away + case '{ (arg: FieldName) => ($body(arg): FieldName).lowercase } => + recurse(body, "lowercase" :: acc) // body: Expr[FieldName => FieldName] + + // $body is the next tree with the '.uppercase' call stripped away + case '{ (arg: FieldName) => ($body(arg): FieldName).uppercase } => + recurse(body, "uppercase" :: acc) // body: Expr[FieldName => FieldName] + + // this matches an identity function, i.e. the end of our loop + case '{ (arg: FieldName) => arg } => acc + end recurse + + recurse(func, Nil) +``` + +For more details on how patterns like `$body(arg)` work please refer to docs section on [the HOAS pattern](https://dotty.epfl.ch/docs/reference/metaprogramming/macros.html#hoas-patterns-1). + +If we were to use this on an expression like this one: +```scala +(name: FieldName) => name.lowercase.uppercase.lowercase +``` +the result would evaluate to `List("lowercase", "uppercase", "lowercase")`. ### Matching types From 21cfaebcf1265c2c87e5800c0ec9fccebc509323 Mon Sep 17 00:00:00 2001 From: Aleksander Rainko Date: Sun, 21 Apr 2024 02:32:00 +0200 Subject: [PATCH 2/2] less repetition, add a missing 'a' --- _overviews/scala3-macros/tutorial/quotes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/_overviews/scala3-macros/tutorial/quotes.md b/_overviews/scala3-macros/tutorial/quotes.md index 904e4b859..c8f662605 100644 --- a/_overviews/scala3-macros/tutorial/quotes.md +++ b/_overviews/scala3-macros/tutorial/quotes.md @@ -270,7 +270,7 @@ But what about the cases where we want more flexibility (eg. we know the subset #### Iterative deconstruction of a function expression -Let's imagine we need a macro that collects names of methods used in an expression of type `FieldName => FieldName`, for a definition of `FieldName` that looks like this: +Let's imagine we need a macro that collects names of methods used in an expression of type `FieldName => FieldName`, for a definition of `FieldName`: ```scala trait FieldName: @@ -299,7 +299,7 @@ def collectUsedMethods(func: Expr[FieldName => FieldName])(using Quotes): List[S recurse(func, Nil) ``` -For more details on how patterns like `$body(arg)` work please refer to docs section on [the HOAS pattern](https://dotty.epfl.ch/docs/reference/metaprogramming/macros.html#hoas-patterns-1). +For more details on how patterns like `$body(arg)` work please refer to a docs section on [the HOAS pattern](https://dotty.epfl.ch/docs/reference/metaprogramming/macros.html#hoas-patterns-1). If we were to use this on an expression like this one: ```scala