Skip to content

Commit 97e36ab

Browse files
author
Lucas Leblanc
committed
Refactor documentation for custom link feature
1 parent cd240a8 commit 97e36ab

File tree

3 files changed

+272
-1
lines changed

3 files changed

+272
-1
lines changed

Diff for: _overviews/core/string-interpolation.md

+262
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
---
2+
layout: singlepage-overview
3+
title: String Interpolation
4+
partof: string-interpolation
5+
6+
languages: [es, ja, zh-cn]
7+
8+
permalink: /overviews/core/:title.html
9+
---
10+
11+
**Josh Suereth**
12+
13+
## Introduction
14+
15+
Starting in Scala 2.10.0, Scala offers a new mechanism to create strings from your data: String Interpolation.
16+
String Interpolation allows users to embed variable references directly in *processed* string literals. Here's an example:
17+
18+
{% tabs example-1 %}
19+
{% tab 'Scala 2 and 3' for=example-1 %}
20+
val name = "James"
21+
println(s"Hello, $name") // Hello, James
22+
{% endtab %}
23+
{% endtabs %}
24+
25+
In the above, the literal `s"Hello, $name"` is a *processed* string literal. This means that the compiler does some additional
26+
work to this literal. A processed string literal is denoted by a set of characters preceding the `"`. String interpolation
27+
was introduced by [SIP-11](https://docs.scala-lang.org/sips/pending/string-interpolation.html), which contains all details of the implementation.
28+
29+
## Usage
30+
31+
Scala provides three string interpolation methods out of the box: `s`, `f` and `raw`.
32+
33+
### The `s` String Interpolator
34+
35+
Prepending `s` to any string literal allows the usage of variables directly in the string. You've already seen an example here:
36+
37+
{% tabs example-2 %}
38+
{% tab 'Scala 2 and 3' for=example-2 %}
39+
val name = "James"
40+
println(s"Hello, $name") // Hello, James
41+
{% endtab %}
42+
{% endtabs %}
43+
44+
Here `$name` is nested inside an `s` processed string. The `s` interpolator knows to insert the value of the `name` variable at this location
45+
in the string, resulting in the string `Hello, James`. With the `s` interpolator, any name that is in scope can be used within a string.
46+
47+
String interpolators can also take arbitrary expressions. For example:
48+
49+
{% tabs example-3 %}
50+
{% tab 'Scala 2 and 3' for=example-3 %}
51+
println(s"1 + 1 = ${1 + 1}")
52+
{% endtab %}
53+
{% endtabs %}
54+
55+
will print the string `1 + 1 = 2`. Any arbitrary expression can be embedded in `${}`.
56+
57+
For some special characters, it is necessary to escape them when embedded within a string.
58+
To represent an actual dollar sign you can double it `$$`, like here:
59+
60+
{% tabs example-4 %}
61+
{% tab 'Scala 2 and 3' for=example-4 %}
62+
println(s"New offers starting at $$14.99")
63+
{% endtab %}
64+
{% endtabs %}
65+
66+
which will print the string `New offers starting at $14.99`.
67+
68+
Double quotes also need to be escaped. This can be done by using triple quotes as shown:
69+
70+
{% tabs example-5 %}
71+
{% tab 'Scala 2 and 3' for=example-5 %}
72+
val person = """{"name":"James"}"""
73+
{% endtab %}
74+
{% endtabs %}
75+
76+
which will produce the string `{"name":"James"}` when printed.
77+
78+
### The `f` Interpolator
79+
80+
Prepending `f` to any string literal allows the creation of simple formatted strings, similar to `printf` in other languages. When using the `f`
81+
interpolator, all variable references should be followed by a `printf`-style format string, like `%d`. Let's look at an example:
82+
83+
{% tabs example-6 %}
84+
{% tab 'Scala 2 and 3' for=example-6 %}
85+
val height = 1.9d
86+
val name = "James"
87+
println(f"$name%s is $height%2.2f meters tall") // James is 1.90 meters tall
88+
{% endtab %}
89+
{% endtabs %}
90+
91+
The `f` interpolator is typesafe. If you try to pass a format string that only works for integers but pass a double, the compiler will issue an
92+
error. For example:
93+
94+
{% tabs f-interpolator-error class=tabs-scala-version %}
95+
96+
{% tab 'Scala 2' for=f-interpolator-error %}
97+
```scala
98+
val height: Double = 1.9d
99+
100+
scala> f"$height%4d"
101+
<console>:9: error: type mismatch;
102+
found : Double
103+
required: Int
104+
f"$height%4d"
105+
^
106+
```
107+
{% endtab %}
108+
109+
{% tab 'Scala 3' for=f-interpolator-error %}
110+
```scala
111+
val height: Double = 1.9d
112+
113+
scala> f"$height%4d"
114+
-- Error: ----------------------------------------------------------------------
115+
1 |f"$height%4d"
116+
| ^^^^^^
117+
| Found: (height : Double), Required: Int, Long, Byte, Short, BigInt
118+
1 error found
119+
120+
```
121+
{% endtab %}
122+
123+
{% endtabs %}
124+
125+
The `f` interpolator makes use of the string format utilities available from Java. The formats allowed after the `%` character are outlined in the
126+
[Formatter javadoc](https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/Formatter.html#detail). If there is no `%` character after a variable
127+
definition a formatter of `%s` (`String`) is assumed.
128+
129+
### The `raw` Interpolator
130+
131+
The raw interpolator is similar to the `s` interpolator except that it performs no escaping of literals within the string. Here's an example processed string:
132+
133+
{% tabs example-7 %}
134+
{% tab 'Scala 2 and 3' for=example-7 %}
135+
scala> s"a\nb"
136+
res0: String =
137+
a
138+
b
139+
{% endtab %}
140+
{% endtabs %}
141+
142+
Here the `s` string interpolator replaced the characters `\n` with a return character. The `raw` interpolator will not do that.
143+
144+
{% tabs example-8 %}
145+
{% tab 'Scala 2 and 3' for=example-8 %}
146+
scala> raw"a\nb"
147+
res1: String = a\nb
148+
{% endtab %}
149+
{% endtabs %}
150+
151+
The raw interpolator is useful when you want to avoid having expressions like `\n` turn into a return character.
152+
153+
In addition to the three default string interpolators, users can define their own.
154+
155+
## Advanced Usage
156+
157+
In Scala, all processed string literals are simple code transformations. Anytime the compiler encounters a string literal of the form:
158+
159+
{% tabs example-9 %}
160+
{% tab 'Scala 2 and 3' for=example-9 %}
161+
id"string content"
162+
{% endtab %}
163+
{% endtabs %}
164+
165+
it transforms it into a method call (`id`) on an instance of [StringContext](https://www.scala-lang.org/api/current/scala/StringContext.html).
166+
This method can also be available on implicit scope.
167+
To define our own string interpolation, we need to create an implicit class (Scala 2) or an `extension` method (Scala 3) that adds a new method to `StringContext`.
168+
Here's an example:
169+
170+
{% tabs json-definition-and-usage class=tabs-scala-version %}
171+
172+
{% tab 'Scala 2' for=json-definition-and-usage %}
173+
```scala
174+
// Note: We extends AnyVal to prevent runtime instantiation. See
175+
// value class guide for more info.
176+
implicit class JsonHelper(val sc: StringContext) extends AnyVal {
177+
def json(args: Any*): JSONObject = sys.error("TODO - IMPLEMENT")
178+
}
179+
180+
def giveMeSomeJson(x: JSONObject): Unit = ...
181+
182+
giveMeSomeJson(json"{ name: $name, id: $id }")
183+
```
184+
{% endtab %}
185+
186+
{% tab 'Scala 3' for=json-definition-and-usage %}
187+
```scala
188+
extension (sc: StringContext)
189+
def json(args: Any*): JSONObject = sys.error("TODO - IMPLEMENT")
190+
191+
def giveMeSomeJson(x: JSONObject): Unit = ...
192+
193+
giveMeSomeJson(json"{ name: $name, id: $id }")
194+
```
195+
{% endtab %}
196+
197+
{% endtabs %}
198+
199+
In this example, we're attempting to create a JSON literal syntax using string interpolation. The `JsonHelper` implicit class must be in scope to use this syntax, and the json method would need a complete implementation. However, the result of such a formatted string literal would not be a string, but a `JSONObject`.
200+
201+
When the compiler encounters the literal `json"{ name: $name, id: $id }"` it rewrites it to the following expression:
202+
203+
{% tabs extension-desugaring class=tabs-scala-version %}
204+
205+
{% tab 'Scala 2' for=extension-desugaring %}
206+
```scala
207+
new StringContext("{ name: ", ", id: ", " }").json(name, id)
208+
```
209+
210+
The implicit class is then used to rewrite it to the following:
211+
212+
```scala
213+
new JsonHelper(new StringContext("{ name: ", ", id: ", " }")).json(name, id)
214+
```
215+
{% endtab %}
216+
217+
{% tab 'Scala 3' for=extension-desugaring %}
218+
```scala
219+
StringContext("{ name: ", ", id: ", " }").json(name, id)
220+
```
221+
{% endtab %}
222+
223+
{% endtabs %}
224+
225+
So, the `json` method has access to the raw pieces of strings and each expression as a value. A simplified (buggy) implementation of this method could be:
226+
227+
{% tabs json-fake-implementation class=tabs-scala-version %}
228+
229+
{% tab 'Scala 2' for=json-fake-implementation %}
230+
```scala
231+
implicit class JsonHelper(val sc: StringContext) extends AnyVal {
232+
def json(args: Any*): JSONObject = {
233+
val strings = sc.parts.iterator
234+
val expressions = args.iterator
235+
val buf = new StringBuilder(strings.next())
236+
while (strings.hasNext) {
237+
buf.append(expressions.next())
238+
buf.append(strings.next())
239+
}
240+
parseJson(buf)
241+
}
242+
}
243+
```
244+
{% endtab %}
245+
246+
{% tab 'Scala 3' for=json-fake-implementation %}
247+
```scala
248+
extension (sc: StringContext)
249+
def json(args: Any*): JSONObject =
250+
val strings = sc.parts.iterator
251+
val expressions = args.iterator
252+
val buf = new StringBuilder(strings.next())
253+
while strings.hasNext do
254+
buf.append(expressions.next())
255+
buf.append(strings.next())
256+
parseJson(buf)
257+
```
258+
{% endtab %}
259+
260+
{% endtabs %}
261+
262+
Each of the string portions of the processed string are exposed in the `StringContext`'s `parts` member. Each of the expression values is passed into the `json` method's `args` parameter. The `json` method takes this and generates a big string which it then parses into JSON. A more sophisticated implementation could avoid having to generate this string and simply construct the JSON directly from the raw strings and expression values.
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: Improving Your Workflow
3+
description: This page describes improving efficiency of debugging the Scala 3 compiler.
4+
redirect_to: https://dotty.epfl.ch/docs/contributing/workflow/efficiency.html
5+
---

Diff for: _overviews/scala3-scaladoc/settings.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,11 @@ Links to social sites. For example:
8989

9090
`-social-links:github::https://github.com/lampepfl/dotty,discord::https://discord.com/invite/scala,twitter::https://twitter.com/scala_lang`
9191

92-
Valid values are of the form: `[github|twitter|gitter|discord]::link`. Scaladoc also supports `custom::link::white_icon_name::black_icon_name`. In this case icons must be present in `images/` directory.
92+
Valid values are of the form: `[github|twitter|gitter|discord]::link`. Scaladoc also supports `custom::link::light_icon_name[::dark_icon_name]`(Dark icon is optional). For example:
93+
94+
`-social-links:scalalang::https://docs.scala-lang.org/::logo-scalalang.png[::logo-scalalang-dark.png]`
95+
96+
In this case icons must be present in `docs/_assets/images/` directory.
9397

9498
##### -skip-by-id
9599

0 commit comments

Comments
 (0)