-
Notifications
You must be signed in to change notification settings - Fork 381
/
Copy pathLexicalScopes.scala
293 lines (288 loc) · 7.4 KB
/
LexicalScopes.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/*
* scala-exercises - exercises-scalatutorial
* Copyright (C) 2015-2016 47 Degrees, LLC. <http://www.47deg.com>
*/
package scalatutorial.sections
/** @param name lexical_scopes */
object LexicalScopes extends ScalaTutorialSection {
/**
* = Nested functions =
*
* It's good functional programming style to split up a task into many small functions.
*
* But the names of functions like `sqrtIter`, `improve`, and `isGoodEnough` (defined in the
* previous section) matter only for the ''implementation'' of `sqrt`, not for its ''usage''.
*
* Normally we would not like users to access these functions directly.
*
* We can achieve this and at the same time avoid “name-space pollution” by
* putting the auxiliary functions inside `sqrt`.
*
* = The `sqrt` Function, Take 2 =
*
* {{{
* def sqrt(x: Double) = {
* def sqrtIter(guess: Double, x: Double): Double =
* if (isGoodEnough(guess, x)) guess
* else sqrtIter(improve(guess, x), x)
*
* def improve(guess: Double, x: Double) =
* (guess + x / guess) / 2
*
* def isGoodEnough(guess: Double, x: Double) =
* math.abs(guess * guess - x) < 0.001
*
* sqrtIter(1.0, x)
* }
* }}}
*
* = Blocks in Scala =
*
* - A block is delimited by braces `{ ... }`.
*
* {{{
* {
* val x = f(3)
* x * x
* }
* }}}
*
* - It contains a sequence of definitions or expressions.
* - The last element of a block is an expression that defines its value.
* - This return expression can be preceded by auxiliary definitions.
* - Blocks are themselves expressions; a block may appear everywhere an expression can.
*
* = Blocks and Visibility =
*
* - The definitions inside a block are only visible from within the block.
* - The definitions inside a block ''shadow'' definitions of the same names
* outside the block.
*
* == Exercise: Scope Rules ==
*
* What is the value of `result` in the following program?
*
*/
def scopeRules(res0: Int): Unit = {
val x = 0
def f(y: Int) = y + 1
val result = {
val x = f(3)
x * x
} + x
result shouldBe res0
}
/**
* = Lexical Scoping =
*
* Definitions of outer blocks are visible inside a block unless they are shadowed.
*
* Therefore, we can simplify `sqrt` by eliminating redundant occurrences of the `x` parameter, which means
* everywhere the same thing:
*
* = The `sqrt` Function, Take 3 =
*
* {{{
* def sqrt(x: Double) = {
* def sqrtIter(guess: Double): Double =
* if (isGoodEnough(guess)) guess
* else sqrtIter(improve(guess))
*
* def improve(guess: Double) =
* (guess + x / guess) / 2
*
* def isGoodEnough(guess: Double) =
* math.abs(guess * guess - x) < 0.001
*
* sqrtIter(1.0)
* }
* }}}
*
* = Semicolons =
*
* In Scala, semicolons at the end of lines are in most cases optional.
*
* You could write:
*
* {{{
* val x = 1;
* }}}
*
* but most people would omit the semicolon.
*
* On the other hand, if there are more than one statements on a line, they need to be
* separated by semicolons:
*
* {{{
* val y = x + 1; y * y
* }}}
*
* = Semicolons and infix operators =
*
* One issue with Scala's semicolon convention is how to write expressions that span
* several lines. For instance:
*
* {{{
* someLongExpression
* + someOtherExpression
* }}}
*
* would be interpreted as ''two'' expressions:
*
* {{{
* someLongExpression;
* + someOtherExpression
* }}}
*
* There are two ways to overcome this problem.
*
* You could write the multi-line expression in parentheses, because semicolons
* are never inserted inside `(…)`:
*
* {{{
* (someLongExpression
* + someOtherExpression)
* }}}
*
* Or you could write the operator on the first line, because this tells the Scala
* compiler that the expression is not yet finished:
*
* {{{
* someLongExpression +
* someOtherExpression
* }}}
*
* = Top-Level Definitions =
*
* In real Scala programs, `def` and `val` definitions must be written
* within a top-level ''object definition'', in .scala file:
*
* {{{
* object MyExecutableProgram {
* val myVal = …
* def myMethod = …
* }
* }}}
*
* The above code defines an ''object'' named `MyExecutableProgram`. You
* can refer to its ''members'' using the usual dot notation:
*
* {{{
* MyExecutableProgram.myMethod
* }}}
*
* The definition of `MyExecutableProgram` is ''top-level'' because it
* is not nested within another definition.
*
* = Packages and Imports =
*
* Top-level definitions can be organized in ''packages''.
* To place a class or object inside a package, use a package clause
* at the top of your source file:
*
* {{{
* // file foo/Bar.scala
* package foo
* object Bar { … }
* }}}
*
* {{{
* // file foo/Baz.scala
* package foo
* object Baz { … }
* }}}
*
* Definitions located in a package are visible from other definitions
* located in the same package:
*
* {{{
* // file foo/Baz.scala
* package foo
* object Baz {
* // Bar is visible because it is in the `foo` package too
* Bar.someMethod
* }
* }}}
*
* On the other hand, definitions located in other packages are not directly
* visible: you must use ''fully qualified names'' to refer to them:
*
* {{{
* // file quux/Quux.scala
* package quux
* object Quux {
* foo.Bar.someMethod
* }
* }}}
*
* Finally, you can import names to avoid repeating their fully qualified form:
*
* {{{
* // file quux/Quux.scala
* package quux
* import foo.Bar
* object Quux {
* // Bar refers to the imported `foo.Bar`
* Bar.someMethod
* }
* }}}
*
* = Automatic Imports =
*
* Some entities are automatically imported in any Scala program.
*
* These are:
*
* - All members of package `scala`
* - All members of package `java.lang`
* - All members of the singleton object `scala.Predef`.
*
* Here are the fully qualified names of some types and functions
* which you have seen so far:
*
* {{{
* Int scala.Int
* Boolean scala.Boolean
* Object java.lang.Object
* String java.lang.String
* }}}
*
* = Writing Executable Programs =
*
* So far our examples of code were executed from your Web
* browser, but it is also possible to create standalone
* applications in Scala.
*
* Each such application contains an object with a `main` method.
*
* For instance, here is the "Hello World!" program in Scala:
*
* {{{
* object Hello {
* def main(args: Array[String]) = println("hello world!")
* }
* }}}
*
* Once this program is compiled, you can start it from the command line with
*
* {{{
* $ scala Hello
* }}}
*
* = Exercise =
*
*/
def objectScopes(res0: Int): Unit = {
object Foo {
val x = 1
}
object Bar {
val x = 2
}
object Baz {
import Bar.x
val y = x + Foo.x
}
Baz.y shouldBe res0
}
}