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
+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.
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+
---

_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)