Skip to content

Commit f491691

Browse files
committed
Add SyntacticConveniences section
1 parent 05a9393 commit f491691

File tree

3 files changed

+321
-1
lines changed

3 files changed

+321
-1
lines changed

src/main/scala/scalatutorial/sections/StandardLibrary.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ object StandardLibrary extends ScalaTutorialSection {
115115
val cond: (Int, Int) => Boolean = res0
116116
def insert(x: Int, xs: List[Int]): List[Int] =
117117
xs match {
118-
case List() => res1
118+
case List() => x :: res1
119119
case y :: ys =>
120120
if (cond(x, y)) x :: y :: ys
121121
else y :: insert(x, ys)

src/main/scala/scalatutorial/sections/SyntacticConveniences.scala

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,281 @@ package scalatutorial.sections
33
/** @param name syntactic_conveniences */
44
object 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
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package scalatutorial.sections
2+
3+
import org.scalacheck.Shapeless._
4+
import org.scalaexercises.Test
5+
import org.scalatest.Spec
6+
import org.scalatest.prop.Checkers
7+
import shapeless.HNil
8+
9+
class SyntacticConveniencesSpec extends Spec with Checkers {
10+
11+
def `check string interpolation`: Unit = {
12+
check(Test.testSuccess(SyntacticConveniences.stringInterpolation _, "Hello, Functional Programming!" :: HNil))
13+
}
14+
15+
def `check string interpolation2`: Unit = {
16+
check(Test.testSuccess(SyntacticConveniences.stringInterpolation2 _, "Hello, SCALA!" :: HNil))
17+
}
18+
19+
def `check tuples`: Unit = {
20+
check(Test.testSuccess(SyntacticConveniences.tupleExtraction _, "foo" :: HNil))
21+
}
22+
23+
def `check tuples2`: Unit = {
24+
check(Test.testSuccess(SyntacticConveniences.tupleExtraction2 _, "foo" :: HNil))
25+
}
26+
27+
def `check tuples manipulation`: Unit = {
28+
check(Test.testSuccess(SyntacticConveniences.tupleManipulation _, "foo" :: HNil))
29+
}
30+
31+
def `check default parameters`: Unit = {
32+
check(Test.testSuccess(SyntacticConveniences.defaultParameters _, 1 :: HNil))
33+
}
34+
35+
def `check repeated parameters`: Unit = {
36+
check(Test.testSuccess(SyntacticConveniences.repeatedParameters _, 2.0 :: HNil))
37+
}
38+
39+
def `check type alias`: Unit = {
40+
check(Test.testSuccess(SyntacticConveniences.typeAlias _, (Right((2, 0)): Either[String, (Int, Int)]) :: HNil))
41+
}
42+
43+
}

0 commit comments

Comments
 (0)