From e67ccc60ab9f7a3e1a540d8778856f57eb5a83e5 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Fri, 18 Nov 2022 19:47:08 +0800 Subject: [PATCH 1/8] add code tabs --- .../scala3-book/ca-given-using-clauses.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index 6abf858e6e..78b91805a1 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -8,16 +8,22 @@ previous-page: ca-contextual-abstractions-intro next-page: types-type-classes --- + Scala 3 offers two important feature for contextual abstraction: - **Using Clauses** allow you to specify parameters that, at the call site, can be omitted by the programmer and should be automatically provided by the context. - **Given Instances** let you define terms that can be used by the Scala compiler to fill in the missing arguments. ## Using Clauses + When designing a system, often context information like _configuration_ or settings need to be provided to the different components of your system. One common way to achieve this is by passing the configuration as additional argument to your methods. In the following example, we define a case class `Config` to model some website configuration and pass it around in the different methods. + +{% tabs nonusing %} +{% tab 'Scala 2 and 3' for=nonusing %} + ```scala case class Config(port: Int, baseUrl: String) @@ -29,11 +35,20 @@ def renderWidget(items: List[String], c: Config): String = ??? val config = Config(8080, "docs.scala-lang.org") renderWebsite("/home", config) ``` + +{% endtab %} +{% endtabs %} + Let us assume that the configuration does not change throughout most of our code base. Passing `c` to each and every method call (like `renderWidget`) becomes very tedious and makes our program more difficult to read, since we need to ignore the `c` argument. #### Using `using` to mark parameters as contextual + In Scala 3, we can mark some parameters of our methods as _contextual_. + +{% tabs using1 %} +{% tab 'Scala 3 Only' for=using1 %} + ```scala def renderWebsite(path: String)(using c: Config): String = "" + renderWidget(List("cart")) + "" @@ -42,6 +57,10 @@ def renderWebsite(path: String)(using c: Config): String = def renderWidget(items: List[String])(using c: Config): String = ??? ``` + +{% endtab %} +{% endtabs %} + By starting a parameter section with the keyword `using`, we tell the Scala compiler that at the callsite it should automatically find an argument with the correct type. The Scala compiler thus performs **term inference**. @@ -50,6 +69,9 @@ So the program is equivalent to the one above. In fact, since we do not need to refer to `c` in our implementation of `renderWebsite` anymore, we can even omit its name in the signature: +{% tabs using2 %} +{% tab 'Scala 3 Only' for=using2 %} + ```scala // no need to come up with a parameter name // vvvvvvvvvvvvv @@ -57,23 +79,38 @@ def renderWebsite(path: String)(using Config): String = "" + renderWidget(List("cart")) + "" ``` +{% endtab %} +{% endtabs %} + #### Explicitly providing contextual arguments + We have seen how to _abstract_ over contextual parameters and that the Scala compiler can provide arguments automatically for us. But how can we specify which configuration to use for our call to `renderWebsite`? Like we specified our parameter section with `using`, we can also explicitly provide contextual arguments with `using:` +{% tabs using3 %} +{% tab 'Scala 3 Only' for=using2 %} + ```scala renderWebsite("/home")(using config) ``` + +{% endtab %} +{% endtabs %} + Explicitly providing contextual parameters can be useful if we have multiple different values in scope that would make sense, and we want to make sure that the correct one is passed to the function. For all other cases, as we will see in the next Section, there is also another way to bring contextual values into scope. ## Given Instances + We have seen that we can explicitly pass arguments as contextual parameters by marking the argument section of the _call_ with `using`. However, if there is _a single canonical value_ for a particular type, there is another preferred way to make it available to the Scala compiler: by marking it as `given`. +{% tabs given1 %} +{% tab 'Scala 3 Only' for=given1 %} + ```scala val config = Config(8080, "docs.scala-lang.org") // this is the type that we want to provide the @@ -84,15 +121,25 @@ given Config = config // this is the value the Scala compiler will infer // as argument to contextual parameters of type Config ``` + +{% endtab %} +{% endtabs %} + In the above example we specify that whenever a contextual parameter of type `Config` is omitted in the current scope, the compiler should infer `config` as an argument. Having defined a given for `Config`, we can simply call `renderWebsite`: +{% tabs given2 %} +{% tab 'Scala 3 Only' for=given2 %} + ```scala renderWebsite("/home") // ^^^^^ // again no argument ``` +{% endtab %} +{% endtabs %} + [reference]: {{ site.scala3ref }}/overview.html [blog-post]: /2020/11/06/explicit-term-inference-in-scala-3.html From 33d87b4a3494dde8eea321c9d885ad32689e9690 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Sat, 19 Nov 2022 02:49:52 +0800 Subject: [PATCH 2/8] Update _overviews/scala3-book/ca-given-using-clauses.md Co-authored-by: Jamie Thompson --- _overviews/scala3-book/ca-given-using-clauses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index 78b91805a1..6f240aab89 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -90,7 +90,7 @@ But how can we specify which configuration to use for our call to `renderWebsite Like we specified our parameter section with `using`, we can also explicitly provide contextual arguments with `using:` {% tabs using3 %} -{% tab 'Scala 3 Only' for=using2 %} +{% tab 'Scala 3 Only' %} ```scala renderWebsite("/home")(using config) From 9cbfa4dbc80f73ad8301e93abbd38247cce095d7 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Sat, 19 Nov 2022 02:50:06 +0800 Subject: [PATCH 3/8] Update _overviews/scala3-book/ca-given-using-clauses.md Co-authored-by: Jamie Thompson --- _overviews/scala3-book/ca-given-using-clauses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index 6f240aab89..da08a81a42 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -70,7 +70,7 @@ So the program is equivalent to the one above. In fact, since we do not need to refer to `c` in our implementation of `renderWebsite` anymore, we can even omit its name in the signature: {% tabs using2 %} -{% tab 'Scala 3 Only' for=using2 %} +{% tab 'Scala 3 Only' %} ```scala // no need to come up with a parameter name From 801bc8118527b81d0b5342a37a0635546e71a060 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Sat, 19 Nov 2022 02:50:26 +0800 Subject: [PATCH 4/8] Update _overviews/scala3-book/ca-given-using-clauses.md Co-authored-by: Jamie Thompson --- _overviews/scala3-book/ca-given-using-clauses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index da08a81a42..e0ec3ab687 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -47,7 +47,7 @@ Passing `c` to each and every method call (like `renderWidget`) becomes very ted In Scala 3, we can mark some parameters of our methods as _contextual_. {% tabs using1 %} -{% tab 'Scala 3 Only' for=using1 %} +{% tab 'Scala 3 Only' %} ```scala def renderWebsite(path: String)(using c: Config): String = From b7f6e7f303f95eb334040c95ba099766f143a5d3 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Sat, 19 Nov 2022 02:50:56 +0800 Subject: [PATCH 5/8] Update _overviews/scala3-book/ca-given-using-clauses.md Co-authored-by: Jamie Thompson --- _overviews/scala3-book/ca-given-using-clauses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index e0ec3ab687..8401808ff1 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -22,7 +22,7 @@ One common way to achieve this is by passing the configuration as additional arg In the following example, we define a case class `Config` to model some website configuration and pass it around in the different methods. {% tabs nonusing %} -{% tab 'Scala 2 and 3' for=nonusing %} +{% tab 'Scala 2 and 3' %} ```scala case class Config(port: Int, baseUrl: String) From 6b18be50910a289d8a646f92191e020f61401284 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Sat, 19 Nov 2022 02:51:08 +0800 Subject: [PATCH 6/8] Update _overviews/scala3-book/ca-given-using-clauses.md Co-authored-by: Jamie Thompson --- _overviews/scala3-book/ca-given-using-clauses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index 8401808ff1..d942f83f1b 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -109,7 +109,7 @@ We have seen that we can explicitly pass arguments as contextual parameters by m However, if there is _a single canonical value_ for a particular type, there is another preferred way to make it available to the Scala compiler: by marking it as `given`. {% tabs given1 %} -{% tab 'Scala 3 Only' for=given1 %} +{% tab 'Scala 3 Only' %} ```scala val config = Config(8080, "docs.scala-lang.org") From 3f7ecf78f4f0d87e128dd222fcd68360b115dfaf Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Sat, 19 Nov 2022 02:51:37 +0800 Subject: [PATCH 7/8] Update _overviews/scala3-book/ca-given-using-clauses.md Co-authored-by: Jamie Thompson --- _overviews/scala3-book/ca-given-using-clauses.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index d942f83f1b..15b05dc856 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -130,7 +130,7 @@ In the above example we specify that whenever a contextual parameter of type `Co Having defined a given for `Config`, we can simply call `renderWebsite`: {% tabs given2 %} -{% tab 'Scala 3 Only' for=given2 %} +{% tab 'Scala 3 Only' %} ```scala renderWebsite("/home") From 413328e59f8d03cc99785859e73afd1ded7a9851 Mon Sep 17 00:00:00 2001 From: Ben Luo Date: Sat, 19 Nov 2022 05:59:57 +0800 Subject: [PATCH 8/8] add "Scala 3 Only" line as first line of the document. --- _overviews/scala3-book/ca-given-using-clauses.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/_overviews/scala3-book/ca-given-using-clauses.md b/_overviews/scala3-book/ca-given-using-clauses.md index 15b05dc856..399a6d1d6f 100644 --- a/_overviews/scala3-book/ca-given-using-clauses.md +++ b/_overviews/scala3-book/ca-given-using-clauses.md @@ -9,6 +9,8 @@ next-page: types-type-classes --- +
Use contextual abstraction Scala 3 Only
+ Scala 3 offers two important feature for contextual abstraction: - **Using Clauses** allow you to specify parameters that, at the call site, can be omitted by the programmer and should be automatically provided by the context.