From e2bed5e5c76db283c936e16b64c5fe63224e262e Mon Sep 17 00:00:00 2001 From: Luc Henninger Date: Tue, 27 Sep 2022 23:41:21 +0200 Subject: [PATCH 1/4] Add code tabs for _tour/annotations --- _tour/annotations.md | 67 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/_tour/annotations.md b/_tour/annotations.md index bdd39a09b7..deea34d72e 100644 --- a/_tour/annotations.md +++ b/_tour/annotations.md @@ -11,7 +11,10 @@ redirect_from: "/tutorials/tour/annotations.html" --- Annotations associate meta-information with definitions. For example, the annotation `@deprecated` before a method causes the compiler to print a warning if the method is used. -``` + +{% tabs annotations_1 class=tabs-scala-version %} +{% tab 'Scala 2' for=annotations_1 %} +```scala mdoc object DeprecationDemo extends App { @deprecated("deprecation message", "release # which deprecates method") def hello = "hola" @@ -19,6 +22,18 @@ object DeprecationDemo extends App { hello } ``` +{% endtab %} +{% tab 'Scala 3' for=annotations_1 %} +```scala +object DeprecationDemo extends App: + @deprecated("deprecation message", "release # which deprecates method") + def hello = "hola" + + hello +``` +{% endtab %} +{% endtabs %} + This will compile but the compiler will print a warning: "there was one deprecation warning". An annotation clause applies to the first definition or declaration following it. More than one annotation clause may precede a definition and declaration. The order in which these clauses are given does not matter. @@ -26,6 +41,9 @@ An annotation clause applies to the first definition or declaration following it ## Annotations that ensure correctness of encodings Certain annotations will actually cause compilation to fail if a condition(s) is not met. For example, the annotation `@tailrec` ensures that a method is [tail-recursive](https://en.wikipedia.org/wiki/Tail_call). Tail-recursion can keep memory requirements constant. Here's how it's used in a method which calculates the factorial: + +{% tabs annotations_2 class=tabs-scala-version %} +{% tab 'Scala 2' for=annotations_2 %} ```scala mdoc import scala.annotation.tailrec @@ -38,8 +56,26 @@ def factorial(x: Int): Int = { factorialHelper(x, 1) } ``` -The `factorialHelper` method has the `@tailrec` which ensures the method is indeed tail-recursive. If we were to change the implementation of `factorialHelper` to the following, it would fail: +{% endtab %} +{% tab 'Scala 3' for=annotations_2 %} +```scala +import scala.annotation.tailrec + +def factorial(x: Int): Int = + + @tailrec + def factorialHelper(x: Int, accumulator: Int): Int = + if x == 1 then accumulator else factorialHelper(x - 1, accumulator * x) + factorialHelper(x, 1) ``` +{% endtab %} +{% endtabs %} + +The `factorialHelper` method has the `@tailrec` which ensures the method is indeed tail-recursive. If we were to change the implementation of `factorialHelper` to the following, it would fail: + +{% tabs annotations_3 class=tabs-scala-version %} +{% tab 'Scala 2' for=annotations_3 %} +```scala mdoc:fail import scala.annotation.tailrec def factorial(x: Int): Int = { @@ -50,12 +86,37 @@ def factorial(x: Int): Int = { factorialHelper(x) } ``` -We would get the message "Recursive call not in tail position". +{% endtab %} +{% tab 'Scala 3' for=annotations_3 %} +```scala +import scala.annotation.tailrec +def factorial(x: Int): Int = + @tailrec + def factorialHelper(x: Int): Int = + if x == 1 then 1 else x * factorialHelper(x - 1) + factorialHelper(x) +``` +{% endtab %} +{% endtabs %} + +We would get the message "Recursive call not in tail position". ## Annotations affecting code generation + +{% tabs annotations_4 class=tabs-scala-version %} +{% tab 'Scala 2' for=annotations_4 %} + Some annotations like `@inline` affect the generated code (i.e. your jar file might have different bytes than if you hadn't used the annotation). Inlining means inserting the code in a method's body at the call site. The resulting bytecode is longer, but hopefully runs faster. Using the annotation `@inline` does not ensure that a method will be inlined, but it will cause the compiler to do it if and only if some heuristics about the size of the generated code are met. +{% endtab %} +{% tab 'Scala 3' for=annotations_4 %} + +Some annotations like `@main` affect the generated code (i.e. your jar file might have different bytes than if you hadn't used the annotation). Inlining means inserting the code in a method's body at the call site. A `@main` annotation on a method turns this method into an executable program. + +{% endtab %} +{% endtabs %} + ### Java Annotations ### When writing Scala code which interoperates with Java, there are a few differences in annotation syntax to note. **Note:** Make sure you use the `-target:jvm-1.8` option with Java annotations. From 30be407f435b2c25b63f0c25122431c1c91b60e2 Mon Sep 17 00:00:00 2001 From: Luc Henninger Date: Tue, 27 Sep 2022 23:45:54 +0200 Subject: [PATCH 2/4] Update annotations.md --- _tour/annotations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tour/annotations.md b/_tour/annotations.md index deea34d72e..9dc560d1be 100644 --- a/_tour/annotations.md +++ b/_tour/annotations.md @@ -14,7 +14,7 @@ Annotations associate meta-information with definitions. For example, the annota {% tabs annotations_1 class=tabs-scala-version %} {% tab 'Scala 2' for=annotations_1 %} -```scala mdoc +```scala mdoc:fail object DeprecationDemo extends App { @deprecated("deprecation message", "release # which deprecates method") def hello = "hola" From d609043d411653a9483b91ac828de6f9b2061a8f Mon Sep 17 00:00:00 2001 From: Luc Henninger Date: Tue, 4 Oct 2022 17:35:50 +0200 Subject: [PATCH 3/4] Update _tour/annotations.md Co-authored-by: Jamie Thompson --- _tour/annotations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_tour/annotations.md b/_tour/annotations.md index 9dc560d1be..448e3083cc 100644 --- a/_tour/annotations.md +++ b/_tour/annotations.md @@ -112,7 +112,7 @@ Some annotations like `@inline` affect the generated code (i.e. your jar file mi {% endtab %} {% tab 'Scala 3' for=annotations_4 %} -Some annotations like `@main` affect the generated code (i.e. your jar file might have different bytes than if you hadn't used the annotation). Inlining means inserting the code in a method's body at the call site. A `@main` annotation on a method turns this method into an executable program. +Some annotations like `@main` affect the generated code (i.e. your jar file might have different bytes than if you hadn't used the annotation). A `@main` annotation on a method generates an executable program that calls the method as an entry point. {% endtab %} {% endtabs %} From 515eb15998b444f76ff3873bc820b9e3690c8f1a Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 5 Oct 2022 11:16:44 +0200 Subject: [PATCH 4/4] add code tabs for java annots --- _tour/annotations.md | 73 ++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/_tour/annotations.md b/_tour/annotations.md index 448e3083cc..98f860ba34 100644 --- a/_tour/annotations.md +++ b/_tour/annotations.md @@ -19,7 +19,7 @@ object DeprecationDemo extends App { @deprecated("deprecation message", "release # which deprecates method") def hello = "hola" - hello + hello } ``` {% endtab %} @@ -123,64 +123,97 @@ When writing Scala code which interoperates with Java, there are a few differenc Java has user-defined metadata in the form of [annotations](https://docs.oracle.com/javase/tutorial/java/annotations/). A key feature of annotations is that they rely on specifying name-value pairs to initialize their elements. For instance, if we need an annotation to track the source of some class we might define it as -``` +{% tabs annotations_5 %} +{% tab 'Java' for=annotations_5 %} +```java @interface Source { - public String URL(); + public String url(); public String mail(); } ``` +{% endtab %} +{% endtabs %} And then apply it as follows -``` -@Source(URL = "https://coders.com/", +{% tabs annotations_6 %} +{% tab 'Java' for=annotations_6 %} +```java +@Source(url = "https://coders.com/", mail = "support@coders.com") -public class MyClass extends TheirClass ... +public class MyJavaClass extends TheirClass ... ``` +{% endtab %} +{% endtabs %} An annotation application in Scala looks like a constructor invocation, for instantiating a Java annotation one has to use named arguments: -``` -@Source(URL = "https://coders.com/", +{% tabs annotations_7 %} +{% tab 'Scala 2 and 3' for=annotations_7 %} +```scala +@Source(url = "https://coders.com/", mail = "support@coders.com") class MyScalaClass ... ``` +{% endtab %} +{% endtabs %} This syntax is quite tedious if the annotation contains only one element (without default value) so, by convention, if the name is specified as `value` it can be applied in Java using a constructor-like syntax: -``` +{% tabs annotations_8 %} +{% tab 'Java' for=annotations_8 %} +```java @interface SourceURL { public String value(); public String mail() default ""; } ``` +{% endtab %} +{% endtabs %} -And then apply it as follows +And then apply it as follows: -``` +{% tabs annotations_9 %} +{% tab 'Java' for=annotations_9 %} +```java @SourceURL("https://coders.com/") -public class MyClass extends TheirClass ... +public class MyJavaClass extends TheirClass ... ``` +{% endtab %} +{% endtabs %} -In this case, Scala provides the same possibility +In this case, Scala provides the same possibility: -``` +{% tabs annotations_10 %} +{% tab 'Scala 2 and 3' for=annotations_10 %} +```scala @SourceURL("https://coders.com/") class MyScalaClass ... ``` +{% endtab %} +{% endtabs %} -The `mail` element was specified with a default value so we need not explicitly provide a value for it. However, if we need to do it we can not mix-and-match the two styles in Java: +The `mail` element was specified with a default value so we need not explicitly provide a value for it. +However, if we need to provide one then in Java we must also explicitly name the `value` parameter: -``` +{% tabs annotations_11 %} +{% tab 'Java' for=annotations_11 %} +```java @SourceURL(value = "https://coders.com/", mail = "support@coders.com") -public class MyClass extends TheirClass ... +public class MyJavaClass extends TheirClass ... ``` +{% endtab %} +{% endtabs %} -Scala provides more flexibility in this respect +Scala provides more flexibility in this respect, so we can choose to only name the `mail` parameter: -``` +{% tabs annotations_12 %} +{% tab 'Scala 2 and 3' for=annotations_12 %} +```scala @SourceURL("https://coders.com/", mail = "support@coders.com") - class MyScalaClass ... +class MyScalaClass ... ``` +{% endtab %} +{% endtabs %}