-
Notifications
You must be signed in to change notification settings - Fork 2
/
scala.txt
467 lines (302 loc) · 9.99 KB
/
scala.txt
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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
SCALA
=====
object HelloWorld {
/** 'main' is your entry point into your program.
* The method signature indicates:
* - argument ('args')
* - argument type (Array[String])
* - return type (Unit, like 'Void')
*/
def main(args : Array[String]) : Unit {
println("Hello, world!")
}
}
object HelloWorld extends App {
// If you extend App, all statements within this object will be run:
println("Hello, World")
}
PACKAGES
--------
// wildcard import
import scala.collection._
// selective import
import scala.collection.{Vector, Sequence}
// renaming import
import scala.collection.{Vector => Vec}
// Naming a package:
package io.olmsted.my.package
VARIABLES
---------
// variable:
var x = 5
// constant:
val y = 8
// explicit type declaration:
val x : Int = 5
FUNCTIONS
---------
def doubler(x : Int): Int =
x * 2 // implicit return
def doubler(x : Int): Int = {
println("Since this has multiple statements, enclose in a block {}")
x * 2
}
// A polymorphic function is parameterized on type 'A'
// 'A' is a generic placeholder that can be used in the method signature.
def polymorphic[A](x : A): A =
x
// e.g. this function accepts an argument of type 'A' and returns a type 'B'
def polymorphic[A](a : A): B
// Higher-order functions can take functions as arguments.
// In this example, it takes a function that accepts 'A' and returns a boolean
def higherOrder[A](a: A, f: (A) => Boolean)
if (f(a)) println("It's true")
else println("It's false")
// Returns an anonymous function that accepts 'A' and returns the result of
// composing the functions f and g.
def compose[A](f: A => A, g: A => A): A =
(x : A) => f(g(a))
def curry[A, B](a : A, f: (A, B) => C): B => C =
(b: B) => f(a, b)
// Lazy (non-strict) evaluation of function arguments
// onTrue and onFalse are no evaluated until called explicitly ('thunks')
def if2[A] (cond: Boolean, onTrue: => A, onFalse => A): A =
if (cond) onTrue else onFalse
ANONYMOUS FUNCTIONS
-------------------
val adder = (x: Int) => x + 1
def doNumberThing(x: Int, operator: (Int) => Int) =
operator(x)
PARTIAL APPLICATION
-------------------
// When used as a parameter, underscore allows you to partially apply a function. In plain English, this means
// deferring passing that unnamed argument to a caller. Here's an example:
def adder(x: Int, y: Int) = y + x
def addTwo = adder(2, _: Int)
addTwo(1) // => 3
CURRYING
--------
// Abstractly defined:
def curry[A, B](a : A, f: (A, B) => C): B => C =
(b: B) => f(a, b)
// More concretely, sometimes it's helpful to let people apply some arguments to a function now, and others
// later:
def multiply(x: Int)(y: Int) = x * y
// Note that the arguments are in separate parens. This allows us to partially apply the function:
multiply(2)(3) // => 6
def multiplyByTwo = multiply(2) _
multiplyByTwo(4) // => 8
CLASSES
-------
class Calculator(brand: String) {
def multiply(x: Int, y: Int): Int = x * y
}
val calc = new Calculator("Casio")
calc.multiply(2, 3) // => 6
/**
* NB: This doesn't work because we didn't do anything with the 'brand'
* constructor argument. You could assign it to a val in the class body,
* but it would be easier and more idiomatic to just use a case class.
*/
calc.brand // => error: value brand is not a member of Calculator
// Inheritance:
class ScientificCalculator(brand: String) extends Calculator(brand) {
def log(n: Double, base: Double): Double = math.log(n) / math.log(base)
}
// If your subclass doesn't add any new behavior, you should use a type alias
// instead.
// Abstract classes:
abstract class ElectronicDevice {
def poweredOn: Boolean
}
class class Calculator(brand: String) extends Electronic {
def multiply(x: Int, y: Int): Int = x * y
def poweredOn: Boolean = true
}
CASE CLASSES
------------
// Case classes are used to conveniently instantiate classes with public contents.
case class Calculator(brand: String) {
def multiply(x: Int, y: Int): Int = x * y
}
// You can omit 'new' when instantiating case classes:
val casioCalc = Calculator("Casio")
casioCalc.brand // => "Casio"
TRAITS
------
// It may be helpful to think of traits more as interfaces than traits / modules.
// A trait may define an interface, implement actual behavior, or some mix of both.
trait Bike {
val make: String
}
trait Brakes {
val brakes = true
}
class Surly extends Bike with Brakes {
val make = "Surly"
}
OBJECTS
-------
// Objects are used instead of classes if we only ever need one instance.
object Counter {
var count = 0
def currentCount(): Int = {
count += 1
count
}
}
Counter.currentCount // => 1
// NB: Classes and objects may have the same name. In this case, they are known as
// companion objects. Companion objects are most commonly used as factories. This
// is a trivial example that just allows you to omit 'new' to create an instance
// using the syntactic sugar provided by 'apply':
class Bar(foo: String)
object Bar {
def apply(foo: String) = new Bar(foo)
}
val bar = Bar
FUNCTIONS ARE OBJECTS
---------------------
// Believe it or not, functions are themselves objects. For example, ever
// argument that takes one argument extends the trait Function1.
object addOne extends Function1[Int, Int] {
def apply(m: Int): Int = m + 1
}
// alternate syntax:
object addOne extends (Int => Int) {
def apply(m: Int): Int = m + 1
}
addOne(3) // => 4
PATTERN MATCHING
----------------
// Often pointed to as one of the most powerful features of the language.
// 1. Matching on values:
val num = 1
num match {
case 1 => "one"
case 2 => "two"
case _ => "Some other number!"
}
// 2. Matching with guards; note that we capture the value with 'n':
val canCount = false
num match {
case n if n == 1 => "one"
case n if n == 2 => "two"
case _ => "Some other number!"
}
// 3. Matching on type:
def typeChecker(value: Any): String = {
value match {
case value: Int => "It's an Int!"
case value: String => "It's a String!"
case _ => "I don't know this type!"
}
}
typeChecker(1) // => "It's an Int!"
// 4. Matching case classes (see case class example above):
val casioCalc = Calculator("Casio")
val hpCalc = Calculator("HP")
calc match {
case Calculator("Casio") => "It's a Casio calculator"
case Calculator("HP") => "It's an HP calculator"
case Calculator(_) => "I'm unfamiliar with this brand"
case _ => "This isn't a calculator at all!"
}
COLLECTIONS
-----------
// Note that collections are typed.
// 1. Lists:
val numbers = List(1, 2, 3, 4, 5)
// 2. Sets (have no duplicates):
val odds = List(1, 3, 5)
// 3. Tuples (two elements, may be different types):
val john = ("John", 29)
john._1 // => "John"
john._2 // => 29
// 4. Maps (keys and values may be typed differently):
val people = Map(
"John" -> 29,
"Galya" -> 28,
"Anna" -> 27
)
OPTIONS
-------
// Options are a nice solution to the 'null pointer' problem. An option may
// contain a value, but if it does not, it provides fallback options.
// Basic option interface:
trait Option[T] {
def isDefined: Boolean
def get: T
def getOrElse(t: T): T
}
// The two most common subtypes of option are Some and None.
// Map.get uses Option as its return type:
val people = Map(
"John" -> 29,
"Galya" -> 28,
"Anna" -> 27
)
people.get("John") // => Option[Int] = Some(2)
people.get("Toby") // => Option[Int] = None (it doesn't exist!)
people.get("Toby").getOrElse(0) // => 0
// Pattern matching is another useful way to handle options
people.get("John") match {
case Some(n) => n
case None => 0
}
FUNCTIONAL COMBINATORS
----------------------
// 'Combinators' are higher-order functions operating over collections.
// 1. map:
val numbers = List(1, 2, 3, 4, 5)
numbers.map((x: Int) => x * 2) // => List(2, 4, 6, 8, 10)
def tripler(x: Int) = x * 3
numbers.map(tripler) // => List(3, 6, 9, 12, 15)
// Mapping over a map (lol):
people.map((person: (String, Int)) => 2016 - person._2)
// => List(1987, 1988, 1989)
// A more readable version with pattern matching:
people.map({case (name, age) => 2016 - age})
// 2. foreach (no return value; side-effecting):
numbers.foreach((x: Int) => println(x))
// 3. filter:
numbers.filter(x: Int) => x % 2 != 0) // List(1, 3, 5)
people.filter({case (name, age) => name != "John"})
// => Map(Galya -> 28, Anna -> 27)
// 4. foldRight (m is the accumulator and its starting value is 0 here):
numbers.foldLeft(0)((m: Int, n: Int) => m + n) // => 15
// 5. foldLeft (same as foldRight but in the opposite direction):
numbers.foldRight(0)((m: Int, n: Int) => m + n) // => 15
FUNCTION COMPOSITION
--------------------
// 'compose' allows you to compose functions. Composed functions are evaluated from the
// 'inside out' (like Lisp), with the return value of inner functions passed up to the
// outer functions.
def first(s: String) = "first(" + s + ")"
def second(s: String) = "second(" + s + ")"
val composed = first _ compose second _
composed("neat") // => "first(second(neat))"
// 'andThen' is like compose, but it evaluates from the outside in:
val andThened = first _ andThen second _
andThened("neat") // => "second(first(neat))"
SBT AND PROJECT CONVENTIONS
---------------------------
// SBT project layout:
* project (project definition files)
* project/build/<yourproject>.scala (main project definition file)
* project/build.properties (project, sbt, and scala version definitions)
* src
* main/scala (your project code here)
* main/resources (any static files you want to include in the jar)
* test/scala (test files)
* lib_managed (jar files your project depends on; populated by sbt update)
* target (destination for generated stuff, like class files and jars)
// Using SBT:
$ sbt test // Run tests
$ sbt console // Start up a REPL with all project dependencies loaded
$ sbt update // Pull latest dependencies
// Adding a dependency to project/build/<yourproject>.scala
class SampleProject(info: ProjectInfo) extends DefaultProject(info) {
val jackson = "org.codehaus.jackson" % "jackson-core-asl" % "1.6.1"
val specs = "org.scala-tools.testing" % "specs_2.8.0" % "1.6.5" % "test"
}