-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy path19_Types.sc
403 lines (319 loc) · 9.68 KB
/
19_Types.sc
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
// 19.0 Introduction
// Variance
class Grandparent
class Parent extends Grandparent
class Child extends Parent
class InvariantClass[A]
class CovariantClass[+A]
class ContravariantClass[-A]
class VarianceExamples {
def invarMethod(x: InvariantClass[Parent]) {}
def covarMethod(x: CovariantClass[Parent]) {}
def contraMethod(x: ContravariantClass[Parent]) {}
// invarMethod(new InvariantClass[Child])
invarMethod(new InvariantClass[Parent])
// invarMethod(new InvariantClass[Grandparent])
covarMethod(new CovariantClass[Child])
covarMethod(new CovariantClass[Parent])
// covarMethod(new CovariantClass[Grandparent])
// contraMethod(new ContravariantClass[Child])
contraMethod(new ContravariantClass[Parent])
contraMethod(new ContravariantClass[Grandparent])
}
// Bounds
A <: B
A >: B
A <: Upper >: Lower
// Type Constraints
A =:= B // A must be equal to B
A <:< B // A must be a subtype of B
A <%< B // A must be viewable as B
// 19.1. Creating Classes That Use Generic Types
class LinkedList[A] {
private class Node[A](elem: A) {
var next: Node[A] = _
override def toString = elem.toString
}
private var head: Node[A] = _
def add(elem: A) {
val n = new Node(elem)
n.next = head
head = n
}
private def printNodes(n: Node[A]) {
if (n != null) {
println(n)
printNodes(n.next)
}
}
def printAll() { printNodes(head) }
}
val ints = new LinkedList[Int]()
ints.add(1)
ints.add(2)
val strings = new LinkedList[String]()
strings.add("Nacho")
strings.add("Libre")
strings.printAll()
trait Animal
class Dog extends Animal { override def toString = "Dog" }
class SuperDog extends Dog { override def toString = "SuperDog" }
class FunnyDog extends Dog { override def toString = "FunnyDog" }
val dogs = new LinkedList[Dog]
val fido = new Dog
val wonderDog = new SuperDog
val scooby = new FunnyDog
dogs.add(fido)
dogs.add(wonderDog)
dogs.add(scooby)
def printDogTypes(dogs: LinkedList[Dog]) { dogs.printAll() }
printDogTypes(dogs)
val superDogs = new LinkedList[SuperDog]
superDogs.add(wonderDog)
// error: this line won't compile
printDogTypes(superDogs)
// from http://docs.oracle.com/javase/tutorial/java/generics/types.html
public interface Pair<K, V> {
public K getKey();
public V getValue();
}
trait Pair[A, B] {
def getKey: A
def getValue: B
}
// 19.2. Creating a Method That Takes a Simple Generic Type
def randomName(names: Seq[String]): String = {
val randomNum = util.Random.nextInt(names.length)
names(randomNum)
}
val names = Seq("Aleka", "Christina", "Tyler", "Molly")
val winner = randomName(names)
def randomElement[A](seq: Seq[A]): A = {
val randomNum = scala.util.Random.nextInt(seq.length)
seq(randomNum)
}
// specify the return type is not necessary
def randomElement[A](seq: Seq[A]) = {
val randomNum = scala.util.Random.nextInt(seq.length)
seq(randomNum)
}
randomElement(Seq("Aleka", "Christina", "Tyler", "Molly"))
randomElement(List(1, 2, 3))
randomElement(List(1.0, 2.0, 3.0))
randomElement(Vector.range('a', 'z'))
// 19.3. Using Duck Typing (Structural Types)
class Dog { def speak() { println("woof") } }
class Klingon { def speak() { println("Qapla!") } }
object DuckTyping extends App {
// XXX: As a word of warning, this technique uses reflection, so you may not want to use it when performance is a concern.
def callSpeak[A <: { def speak(): Unit }](obj: A) {
obj.speak()
}
callSpeak(new Dog)
callSpeak(new Klingon)
}
DuckTyping.main(Array())
// 19.4. Make Mutable Collections Invariant
trait Animal { def speak }
class Dog(var name: String) extends Animal {
def speak { println("woof") }
override def toString = name
}
class SuperDog(name: String) extends Dog(name) {
def useSuperPower { println("Using my superpower!") }
}
val fido = new Dog("Fido")
val wonderDog = new SuperDog("Wonder Dog")
val shaggy = new SuperDog("Shaggy")
import scala.collection.mutable.ArrayBuffer
val dogs = ArrayBuffer[Dog]()
dogs += fido
dogs += wonderDog
def makeDogsSpeak(dogs: ArrayBuffer[Dog]) {
dogs.foreach(_.speak)
}
makeDogsSpeak(dogs)
val superDogs = ArrayBuffer[SuperDog]()
superDogs += shaggy
superDogs += wonderDog
makeDogsSpeak(superDogs) // ERROR: won't compile
// Mutable -> invariant
class Array[T]
class ArrayBuffer[A]
class ListBuffer[A]
// Immutable -> covariant
class List[+T]
class Vector[+A]
trait Seq[+A]
// 19.5. Make Immutable Collections Covariant
trait Animal {
def speak
}
class Dog(var name: String) extends Animal {
def speak { println("Dog says woof") }
}
class SuperDog(name: String) extends Dog(name) {
override def speak { println("I'm a SuperDog") }
}
def makeDogsSpeak(dogs: Seq[Dog]) {
dogs.foreach(_.speak)
}
// this works
val dogs = Seq(new Dog("Fido"), new Dog("Tanner"))
makeDogsSpeak(dogs)
// this works too
val superDogs = Seq(new SuperDog("Wonder Dog"), new SuperDog("Scooby"))
makeDogsSpeak(superDogs)
class Container[+A](val elem: A)
def makeDogsSpeak(dogHouse: Container[Dog]) {
dogHouse.elem.speak
}
val dogHouse = new Container(new Dog("Tanner"))
makeDogsSpeak(dogHouse)
val superDogHouse = new Container(new SuperDog("Wonder Dog"))
makeDogsSpeak(superDogHouse)
// 19.6. Create a Collection Whose Elements Are All of Some Base Type
trait CrewMember
class Officer extends CrewMember
class RedShirt extends CrewMember
trait Captain
trait FirstOfficer
trait ShipsDoctor
trait StarfleetTrained
val kirk = new Officer with Captain
val spock = new Officer with FirstOfficer
val bones = new Officer with ShipsDoctor
val officers = new Crew[Officer]()
officers += kirk
officers += spock
officers += bones
val redShirt = new RedShirt
officers += redShirt // ERROR: this won't compile
class Crew[A <: CrewMember] extends ArrayBuffer[A]
val officers = new Crew[Officer]()
// error: won't compile
val officers = new Crew[String]()
val redshirts = new Crew[RedShirt]()
class Crew[A <: CrewMember with StarfleetTrained] extends ArrayBuffer[A]
val kirk = new Officer with Captain with StarfleetTrained
val spock = new Officer with FirstOfficer with StarfleetTrained
val bones = new Officer with ShipsDoctor with StarfleetTrained
val officers = new Crew[Officer with StarfleetTrained]()
officers += kirk
officers += spock
officers += bones
class StarfleetOfficer extends Officer with StarfleetTrained
val kirk = new StarfleetOfficer with Captain
trait CrewMember {
def beamDown { println("beaming down") }
}
class RedShirt extends CrewMember {
def putOnRedShirt { println("putting on my red shirt") }
}
def beamDown[A <: CrewMember](crewMember: Crew[A]) {
crewMember.foreach(_.beamDown)
}
def getReadyForDay[A <: RedShirt](redShirt: Crew[A]) {
redShirt.foreach(_.putOnRedShirt)
}
// <% view bounnd
// <: upper bound
// 19.7. Selectively Adding New Behavior to a Closed Model
def add[A](x: A, y: A)(implicit numeric: Numeric[A]): A = numeric.plus(x, y)
add(1, 1)
add(1.0, 1.5)
add(1, 1.5F)
// Creating a type class
package typeclassdemo
// an existing, closed model
trait Animal
final case class Dog(name: String) extends Animal
final case class Cat(name: String) extends Animal
object Humanish {
// the type class.
// defines an abstract method named 'speak'.
trait HumanLike[A] {
def speak(speaker: A): Unit
}
// companion object
object HumanLike {
// implement the behavior for each desired type. in this case, only for a Dog.
implicit object DogIsHumanLike extends HumanLike[Dog] {
def speak(dog: Dog) { println(s"I'm a Dog, my name is ${dog.name}") }
}
}
}
object TypeClassDemo extends App {
import Humanish.HumanLike
// create a method to make an animal speak
def makeHumanLikeThingSpeak[A](animal: A)(implicit humanLike: HumanLike[A]) {
humanLike.speak(animal)
}
// because HumanLike implemented this for a Dog, it will work
makeHumanLikeThingSpeak(Dog("Rover"))
// however, the method won't compile for a Cat (as desired)
//makeHumanLikeThingSpeak(Cat("Morris"))
}
TypeClassDemo.main(Array())
// 19.8. Building Functionality with Types
// Example 1: Creating a Timer
def timer[A](blockOfCode: => A) = {
val startTime = System.nanoTime
val result = blockOfCode
val stopTime = System.nanoTime
val delta = stopTime - startTime
(result, delta / 1000000d)
}
val (result, time) = timer{ println("Hello") }
println(s"result: $result, time: $time")
def readFile(filename: String) = scala.io.Source.fromFile(filename).getLines
val (result, time) = timer{ readFile("/etc/passwd") }
// Example 2: Writing Your Own “Try” Classes
// version 1
{
sealed class Attempt[A]
final case class Failed[A](val exception: Throwable) extends Attempt[A]
final case class Succeeded[A](value: A) extends Attempt[A]
object Attempt {
def apply[A](f: => A): Attempt[A] =
try {
val result = f
return Succeeded(result)
} catch {
case e: Exception => Failed(e)
}
}
}
val x = Attempt("10".toInt) // Succeeded(10)
val y = Attempt("10A".toInt) // Failed(Exception)
// version 2
{
sealed abstract class Attempt[A] {
var isSuccess = false
// B >: A is a lower bound
def getOrElse[B >: A](default: => B): B = if (isSuccess) get else default
def get: A
}
object Attempt {
def apply[A](f: => A): Attempt[A] =
try {
val result = f
Succeeded(result)
} catch {
case e: Exception => Failed(e)
}
}
final case class Failed[A](val exception: Throwable) extends Attempt[A] {
isSuccess = false
def get: A = throw exception
}
final case class Succeeded[A](result: A) extends Attempt[A] {
isSuccess = true
def get = result
}
}
// The Scala 2.10 Try classes
def getOrElse[U >: T](default: => U): U = if (isSuccess) get else default
def map[U](f: T => U): Try[U] = Try[U](f(value))
// A <:< B “A must be a subtype of B.”
def flatten[U](implicit ev: T <:< Try[U]): Try[U] = value