@@ -3,4 +3,281 @@ package scalatutorial.sections
33/** @param name syntactic_conveniences */
44object SyntacticConveniences extends ScalaTutorialSection {
55
6+ /**
7+ * This section introduces several syntactic sugars supported
8+ * by the language.
9+ *
10+ * = String Interpolation =
11+ *
12+ * To splice values into constant `String` at runtime, you can
13+ * use ''string interpolation'':
14+ */
15+ def stringInterpolation (res0 : String ): Unit = {
16+ def greet (name : String ): String =
17+ s " Hello, $name! "
18+
19+ greet(" Scala" ) shouldBe " Hello, Scala!"
20+ greet(" Functional Programming" ) shouldBe res0
21+ }
22+
23+ /**
24+ * After having prefixed the string literal with `s` you can introduce
25+ * dynamic values in it with `$`.
26+ *
27+ * If you want to splice a complex expression (more than just an identifier),
28+ * surround it with braces:
29+ */
30+ def stringInterpolation2 (res0 : String ): Unit = {
31+ def greet (name : String ): String =
32+ s " Hello, ${name.toUpperCase}! "
33+
34+ greet(" Scala" ) shouldBe res0
35+ }
36+
37+ /**
38+ * = Tuples =
39+ *
40+ * We saw earlier that case class are useful to aggregate information.
41+ * However, sometimes you want to aggregate information without having to define
42+ * a complete case class for it. In such a case you can use ''tuples'':
43+ */
44+ def tuples (res0 : (Int , String )): Unit = {
45+ def pair (i : Int , s : String ): (Int , String ) = (i, s)
46+
47+ pair(42 , " foo" ) shouldBe (42 , " foo" )
48+ pair(0 , " bar" ) shouldBe res0
49+ }
50+
51+ /**
52+ * In the above example, the type `(Int, String)` represents a pair whose
53+ * first element is an `Int` and whose second element is a `String`.
54+ *
55+ * Similarly, the value `(i, s)` is a pair whose first element is `i` and
56+ * whose second element is `s`.
57+ *
58+ * More generally, a type `(T1, …, Tn)` is a ''tuple type'' of n elements
59+ * whose i^th^ element has type `Ti`.
60+ *
61+ * And a value `(t1, … tn)` is a ''tuple value'' of n elements.
62+ *
63+ * == Manipulating Tuples ==
64+ *
65+ * You can retrieve the elements of a tuple by using a ''tuple pattern'':
66+ */
67+ def tupleExtraction (res0 : String ): Unit = {
68+ val is : (Int , String ) = (42 , " foo" )
69+
70+ is match {
71+ case (i, s) =>
72+ i shouldBe 42
73+ s shouldBe res0
74+ }
75+ }
76+
77+ /**
78+ * Or, simply:
79+ */
80+ def tupleExtraction2 (res0 : String ): Unit = {
81+ val is : (Int , String ) = (42 , " foo" )
82+
83+ val (i, s) = is
84+ i shouldBe 42
85+ s shouldBe res0
86+ }
87+
88+ /**
89+ * Alternatively, you can retrieve the 1st element with the `_1` member,
90+ * the 2nd element with the `_2` member, etc:
91+ */
92+ def tupleManipulation (res0 : String ): Unit = {
93+ val is : (Int , String ) = (42 , " foo" )
94+ is._1 shouldBe 42
95+ is._2 shouldBe res0
96+ }
97+
98+ /**
99+ * = `for` expressions =
100+ *
101+ * You probably noticed that several data types of the standard library
102+ * have methods named `map`, `flatMap` and `filter`.
103+ *
104+ * These methods are so common in practice that Scala supports a dedicated
105+ * syntax: ''for expressions''.
106+ *
107+ * == `map` ==
108+ *
109+ * Thus, instead of writing the following:
110+ *
111+ * {{{
112+ * xs.map(x => x + 1)
113+ * }}}
114+ *
115+ * You can write:
116+ *
117+ * {{{
118+ * for (x <- xs) yield x + 1
119+ * }}}
120+ *
121+ * You can read it as “for every value, that I name ‘x’, in ‘xs’, return ‘x + 1’”.
122+ *
123+ * == `filter` ==
124+ *
125+ * Also, instead of writing the following:
126+ *
127+ * {{{
128+ * xs.filter(x => x % 2 == 0)
129+ * }}}
130+ *
131+ * You can write:
132+ *
133+ * {{{
134+ * for (x <- xs if x % 2 == 0) yield x
135+ * }}}
136+ *
137+ * The benefit of this syntax becomes more apparent when it is combined
138+ * with the previous one:
139+ *
140+ * {{{
141+ * for (x <- xs if x % 2 == 0) yield x + 1
142+ *
143+ * // Equivalent to the following:
144+ * xs.filter(x => x % 2 == 0).map(x => x + 1)
145+ * }}}
146+ *
147+ * == `flatMap` ==
148+ *
149+ * Finally, instead of writing the following:
150+ *
151+ * {{{
152+ * xs.flatMap(x => ys.map(y => (x, y)))
153+ * }}}
154+ *
155+ * You can write:
156+ *
157+ * {{{
158+ * for (x <- xs; y <- ys) yield (x, y)
159+ * }}}
160+ *
161+ * You can read it as “for every value ‘x’ in ‘xs’, and then for
162+ * every value ‘y’ in ‘ys’, return ‘(x, y)’”.
163+ *
164+ * == Putting Things Together ==
165+ *
166+ * Here is an example that puts everything together:
167+ *
168+ * {{{
169+ * for {
170+ * x <- xs if x % 2 == 0
171+ * y <- ys
172+ * } yield (x, y)
173+ * }}}
174+ *
175+ * The equivalent de-sugared code is the following:
176+ *
177+ * {{{
178+ * xs.filter { x =>
179+ * x % 2 == 0
180+ * }.flatMap { x =>
181+ * ys.map { y =>
182+ * (x, y)
183+ * }
184+ * }
185+ * }}}
186+ *
187+ * = Method’s Parameters =
188+ *
189+ * == Named Parameters ==
190+ *
191+ * It can sometimes be difficult to figure out what is the meaning of
192+ * each parameter passed to a function. Consider for instance the following
193+ * expression:
194+ *
195+ * {{{
196+ * Range(1, 10, 2)
197+ * }}}
198+ *
199+ * What does it mean? We can improve the readability by using ''named
200+ * parameters''.
201+ *
202+ * Based on the fact that the `Range` constructor is defined as follows:
203+ *
204+ * {{{
205+ * case class Range(start: Int, end: Int, step: Int)
206+ * }}}
207+ *
208+ * We can rewrite our expression as follows:
209+ *
210+ * {{{
211+ * Range(start = 1, end = 10, step = 2)
212+ * }}}
213+ *
214+ * It is now clearer that this expression defines a range of numbers
215+ * from 1 to 10 by increments of 2.
216+ *
217+ * == Default Values ==
218+ *
219+ * Methods’ parameters can have default values. Let’s refine the `Range`
220+ * constructor:
221+ *
222+ * {{{
223+ * case class Range(start: Int, end: Int, step: Int = 1)
224+ * }}}
225+ *
226+ * Here, we say that the `step` parameter has a default value, `1`.
227+ *
228+ * Then, at use site we can omit to supply this parameter and the compiler
229+ * will supply it for us, by using its default value:
230+ */
231+ def defaultParameters (res0 : Int ): Unit = {
232+ case class Range (start : Int , end : Int , step : Int = 1 )
233+
234+ val xs = Range (start = 1 , end = 10 )
235+
236+ xs.step shouldBe res0
237+ }
238+
239+ /**
240+ * == Repeated Parameters ==
241+ *
242+ * You can define a function that can receive an arbitrary number of
243+ * parameters (of the same type) as follows:
244+ */
245+ def repeatedParameters (res0 : Double ): Unit = {
246+ def average (x : Int , xs : Int * ): Double =
247+ (x :: xs.to[List ]).sum.toDouble / (xs.size + 1 )
248+
249+ average(1 ) shouldBe 1.0
250+ average(1 , 2 ) shouldBe 1.5
251+ average(1 , 2 , 3 ) shouldBe res0
252+ }
253+
254+ /**
255+ * The `average` function takes at least one `Int` parameter and then
256+ * an arbitrary number of other values and computes their average.
257+ * By forcing users to supply at least one parameter, we make it impossible
258+ * for them to compute the average of an empty list of numbers.
259+ *
260+ * Sometimes you want to supply each element of a list as as many parameters.
261+ * You can do that by adding a `: _*` type ascription to your list:
262+ *
263+ * {{{
264+ * val xs: List[Int] = …
265+ * average(1, xs: _*)
266+ * }}}
267+ *
268+ * = Type Aliases =
269+ *
270+ * In the same way as you can give meaningful names to expressions,
271+ * you can give meaningful names to ''type expressions'':
272+ */
273+ def typeAlias (res0 : Either [String , (Int , Int )]): Unit = {
274+ type Result = Either [String , (Int , Int )]
275+ def divide (dividend : Int , divisor : Int ): Result =
276+ if (divisor == 0 ) Left (" Division by zero" )
277+ else Right ((dividend / divisor, dividend % divisor))
278+ divide(6 , 4 ) shouldBe Right ((1 , 2 ))
279+ divide(2 , 0 ) shouldBe Left (" Division by zero" )
280+ divide(8 , 4 ) shouldBe res0
281+ }
282+
6283}
0 commit comments