Skip to content

Commit 27947ff

Browse files
committed
Tabs for Scala 2/3 in core/string-interpolation.
1 parent f889af2 commit 27947ff

File tree

1 file changed

+112
-32
lines changed

1 file changed

+112
-32
lines changed

_overviews/core/string-interpolation.md

Lines changed: 112 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,36 @@ interpolator, all variable references should be followed by a `printf`-style for
6767
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
6868
error. For example:
6969

70-
val height: Double = 1.9d
70+
{% tabs f-interpolator-error class=tabs-scala-version %}
7171

72-
scala> f"$height%4d"
73-
<console>:9: error: type mismatch;
74-
found : Double
75-
required: Int
76-
f"$height%4d"
77-
^
72+
{% tab 'Scala 2' for=f-interpolator-error %}
73+
```scala
74+
val height: Double = 1.9d
75+
76+
scala> f"$height%4d"
77+
<console>:9: error: type mismatch;
78+
found : Double
79+
required: Int
80+
f"$height%4d"
81+
^
82+
```
83+
{% endtab %}
84+
85+
{% tab 'Scala 3' for=f-interpolator-error %}
86+
```scala
87+
val height: Double = 1.9d
88+
89+
scala> f"$height%4d"
90+
-- Error: ----------------------------------------------------------------------
91+
1 |f"$height%4d"
92+
| ^^^^^^
93+
| Found: (height : Double), Required: Int, Long, Byte, Short, BigInt
94+
1 error found
95+
96+
```
97+
{% endtab %}
98+
99+
{% endtabs %}
78100

79101
The `f` interpolator makes use of the string format utilities available from Java. The formats allowed after the `%` character are outlined in the
80102
[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
@@ -105,42 +127,100 @@ In Scala, all processed string literals are simple code transformations. Anyti
105127
id"string content"
106128

107129
it transforms it into a method call (`id`) on an instance of [StringContext](https://www.scala-lang.org/api/current/scala/StringContext.html).
108-
This method can also be available on implicit scope. To define our own string interpolation, we simply need to create an implicit class that adds a new method
109-
to `StringContext`. Here's an example:
130+
This method can also be available on implicit scope.
131+
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`.
132+
Here's an example:
110133

111-
// Note: We extends AnyVal to prevent runtime instantiation. See
112-
// value class guide for more info.
113-
implicit class JsonHelper(val sc: StringContext) extends AnyVal {
114-
def json(args: Any*): JSONObject = sys.error("TODO - IMPLEMENT")
115-
}
134+
{% tabs json-definition-and-usage class=tabs-scala-version %}
116135

117-
def giveMeSomeJson(x: JSONObject): Unit = ...
136+
{% tab 'Scala 2' for=json-definition-and-usage %}
137+
```scala
138+
// Note: We extends AnyVal to prevent runtime instantiation. See
139+
// value class guide for more info.
140+
implicit class JsonHelper(val sc: StringContext) extends AnyVal {
141+
def json(args: Any*): JSONObject = sys.error("TODO - IMPLEMENT")
142+
}
118143

119-
giveMeSomeJson(json"{ name: $name, id: $id }")
144+
def giveMeSomeJson(x: JSONObject): Unit = ...
145+
146+
giveMeSomeJson(json"{ name: $name, id: $id }")
147+
```
148+
{% endtab %}
149+
150+
{% tab 'Scala 3' for=json-definition-and-usage %}
151+
```scala
152+
extension (sc: StringContext)
153+
def json(args: Any*): JSONObject = sys.error("TODO - IMPLEMENT")
154+
155+
def giveMeSomeJson(x: JSONObject): Unit = ...
156+
157+
giveMeSomeJson(json"{ name: $name, id: $id }")
158+
```
159+
{% endtab %}
160+
161+
{% endtabs %}
120162

121163
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`.
122164

123165
When the compiler encounters the literal `json"{ name: $name, id: $id }"` it rewrites it to the following expression:
124166

125-
new StringContext("{ name: ", ", id: ", " }").json(name, id)
167+
{% tabs extension-desugaring class=tabs-scala-version %}
168+
169+
{% tab 'Scala 2' for=extension-desugaring %}
170+
```scala
171+
new StringContext("{ name: ", ", id: ", " }").json(name, id)
172+
```
126173

127174
The implicit class is then used to rewrite it to the following:
128175

129-
new JsonHelper(new StringContext("{ name: ", ", id: ", " }")).json(name, id)
130-
131-
So, the `json` method has access to the raw pieces of strings and each expression as a value. A simple (buggy) implementation of this method could be:
132-
133-
implicit class JsonHelper(val sc: StringContext) extends AnyVal {
134-
def json(args: Any*): JSONObject = {
135-
val strings = sc.parts.iterator
136-
val expressions = args.iterator
137-
var buf = new StringBuilder(strings.next())
138-
while(strings.hasNext) {
139-
buf.append(expressions.next())
140-
buf.append(strings.next())
141-
}
142-
parseJson(buf)
143-
}
176+
```scala
177+
new JsonHelper(new StringContext("{ name: ", ", id: ", " }")).json(name, id)
178+
```
179+
{% endtab %}
180+
181+
{% tab 'Scala 3' for=extension-desugaring %}
182+
```scala
183+
StringContext("{ name: ", ", id: ", " }").json(name, id)
184+
```
185+
{% endtab %}
186+
187+
{% endtabs %}
188+
189+
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:
190+
191+
{% tabs json-fake-implementation class=tabs-scala-version %}
192+
193+
{% tab 'Scala 2' for=json-fake-implementation %}
194+
```scala
195+
implicit class JsonHelper(val sc: StringContext) extends AnyVal {
196+
def json(args: Any*): JSONObject = {
197+
val strings = sc.parts.iterator
198+
val expressions = args.iterator
199+
var buf = new StringBuilder(strings.next())
200+
while (strings.hasNext) {
201+
buf.append(expressions.next())
202+
buf.append(strings.next())
144203
}
204+
parseJson(buf)
205+
}
206+
}
207+
```
208+
{% endtab %}
209+
210+
{% tab 'Scala 3' for=json-fake-implementation %}
211+
```scala
212+
extension (sc: StringContext)
213+
def json(args: Any*): JSONObject =
214+
val strings = sc.parts.iterator
215+
val expressions = args.iterator
216+
var buf = new StringBuilder(strings.next())
217+
while strings.hasNext do
218+
buf.append(expressions.next())
219+
buf.append(strings.next())
220+
parseJson(buf)
221+
```
222+
{% endtab %}
223+
224+
{% endtabs %}
145225

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

0 commit comments

Comments
 (0)