The Scala API

Download Report

Transcript The Scala API

The Scala API
The Scala API

Scala has a reputation of being a difficult language


Some people feel that Scala is beyond what the average
programmer can master
Others say it’s no more difficult than Java


Everyone agrees that the Scala API is harder to
understand than the Java API



Some things have been added, but a lot has been simplified
This certainly makes Scala seem more difficult than it is
You don’t have to understand everything about a method in
order to use it effectively
This lecture is an attempt to de-mystify the Scala API
2
Searching


The API is search oriented; every frame has a search field
You can click on a letter and search for methods
3
Icons




C indicates a class
T indicates a trait
O indicates an object
O and C together indicate a
class with a companion object
4
Objects


A class is a template for creating objects
But if you need only one object of a given type, you can just
create it directly


object Instructor { val name = "David Matuszek" }
Scala provides a number of predefined objects, such as Console


object TryIt {
def main(args: Array[String]): Unit = {
val c = Console
c.print("Hello, console!")
}
}
Hello, console!
5
Classes

A class is a template for creating objects; it may take
parameters

Classes may be marked as case, abstract, or final



case classes have special features
 To create an object, you may omit the word new
 Case classes can be used in pattern matching
 Case classes have automatically generated toString,
hashCode, and equals methods (which use the constructor
parameters)
abstract classes cannot be instantiated
final classes cannot be subclassed
6
Creating new objects from classes

When you define a class, you usually use the word new to create objects of that class


If you make the class a case class, you don’t need the word new


object TryIt {
def main(args: Array[String]): Unit = {
val t = new Thing(1)
}
}
class Thing(number: Int) {
println(s"I'm Thing $number!")
}
object TryIt {
def main(args: Array[String]): Unit = {
val t = Thing(1)
}
}
case class Thing(number: Int) {
println(s"I'm Thing $number!")
}
Both objects produce this output: I'm Thing 1!
7
Companion objects and classes


If a class and an object have the same name and are defined on the same source
file, they are companions
Each has access to all the features of the other


object Thing {
private var count = 0
def main(args: Array[String]): Unit = {
new Thing(1, 2, 3)
println(s"That's $count Things!")
}
}
class Thing(val numbers: Int*) {
for (n <- numbers) {
println(s"I'm Thing $n!")
Thing.count += 1
}
}
I'm Thing 1!
I'm Thing 2!
I'm Thing 3!
That's 3 Things!
8
Companion objects and static


Values and methods in a companion object are similar to
static values and methods in Java
The companion object is often a good place to put factory
methods


object Thing {
def main(args: Array[String]): Unit = {
val t = thingMaker(5)
println(t)
}
def thingMaker(n: Int) = new Thing(n)
}
case class Thing(val number: Int)
Thing(5)
9
The apply method

The apply method of a companion object typically creates an object of the
companion class



The apply method of an object typically indexes into that object




scala> val nums = List.apply(1, 2, 3, 4, 5)
nums: List[Int] = List(1, 2, 3, 4, 5)
scala> val m = Map.apply(1 -> 10, 2 -> 20)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2
-> 20)
scala> m.apply(2)
res10: Int = 20
scala> nums.apply(2)
res9: Int = 3
thing(arg) gets translated into thing.apply(arg)
A case class has an apply method; this is why you don’t need new
10
Traits


A trait is like a Java interface, except that it can have concrete as
well as abstract methods
Traits can be “mixed in” to classes and objects


trait Stretchable {
def stretch(s: String) = s.toList.mkString(" ")
}
object Thing extends Stretchable {
def main(args: Array[String]) {
val hi = "Hello, World!"
println(stretch(hi))
}
}
H e l l o ,
W o r l d !
11




Int is in the scala package
It is abstract, so you can’t directly create an Int
It is final, so you can’t create a subclass of Int
It extends AnyVal, which is the superclass of values that
in Java are “primitives”


AnyVal extends Any, which is the “topmost” class
AnyRef also extends Any, and is the class that corresponds to
Java’s Object class
12
Operators are methods


Unlike Java, operators are methods
This means that they are all in the Scala API

In Scala, a name can be:





Composed of letters, digits, and underscores
Composed of “punctuation marks,” excluding brackets and periods
Composed of letters, digits, and underscores, then an underscore, then
punctuation marks
Composed of just about anything (even whitespace) enclosed in backquotes
For example, in the description of Any:

final def ==(arg0: Any): Boolean


Test two objects for equality. The expression x == that is equivalent to if
(x eq null) that eq null else x.equals(that).
returns true if the receiver object is equivalent to the argument; false
otherwise.
13
Parameters and return values

scala> class Animal
defined class Animal
scala> class Mammal extends Animal
defined class Mammal
scala> class Dog extends Mammal
defined class Dog

scala> object Tester {
| def identity(m: Mammal): Mammal = m
| def test = {
|
val something: Animal = identity(new Dog)
|
something
| } }
defined module Tester

scala> Tester.test
res2: Animal = Dog@563625d0
14
Function1

Function1 is declared in the Scala API as
trait Function1[-T1, +R] extends AnyRef




The –T1 indicates that the parameter may be a supertype (this is contravariance)
The +R indicates that the return value may be a subtype (this is covariance)
Functions are contravariant in their argument types and co-variant in their return types
class Animal
class Dog extends Animal
class Person
class Student extends Person
def owner(f: Dog => Person) { println("ok") }
def dogOwner(d: Dog) = new Person
def petOwner(d: Dog) = new Student
def animalOwner(a: Animal) = new Person

// exactly as expected
// return is subtype
// arg is supertype
The following calls are all legal, and all print ok:



owner(dogOwner)
owner(petOwner)
owner(animalOwner)
15
Contravariance in parameters

On the previous slide,


Dog extends Animal and Student extends Person
We also had the definitions:

def owner(f: Dog => Person) { println("ok") }




def dogOwner(d: Dog) = new Person
This is a function of exactly the type required, so it works
def petOwner(d: Dog) = new Student


The argument to this function has type Dog => Person
This is a function Dog => Student, but Student is a subclass of Person,
so it works (covariance)
def animalOwner(a: Animal) = new Person


This is a function Animal => Person, but Animal is a superclass of Dog
Since this function will take any Animal as a parameter, it will work for a Dog
(this is contravariance), and we can use it as a parameter to owner
16
Java isn’t type safe



class Animal {}
class Mammal extends Animal {}
public class Animals {
public static void main(String[] args) {
Mammal[] mammals = new Mammal[1];
Animal[] animals = mammals;
animals[0] = new Animal();
System.out.println(animals[0]);
}
}
This compiles without any problem
Exception in thread "main" java.lang.ArrayStoreException:
Animal
at Animals.main(Animals.java:9)
17
Covariance, contravariance, invariance


Covariance and contravariance are properties of collections
A collection of values of type A is covariant if it may be treated
as a collection of values of some supertype of A




A collection of values of type A is contravariant if it may be
treated as a collection of values of some subtype of A




That is, you can use a subtype of the expected type
Lists are covariant because a List[Dog] may be treated as if it were a
List[Animal]
class List [+A] extends LinearSeq[A]
That is, you can use a supertype of the expected type
trait Function1 [-T1, +R] extends AnyRef
This trait defines a function that is contravariant in its argument type, and
covariant in its return type
A collection is invariant if it is neither covariant or contravariant
18
Variance for mutable structures

For type safety




Read-only (source) data structures can be covariant
Write-only data types (sinks) can be contravariant
If a data structure is both readable and writeable (such as arrays in Java), it
should be invariant
Java arrays are covariant



Did Java get it wrong?
If arrays were invariant, generic operations that do not depend on the type of
object (shuffling an array, comparing two arrays for equality) would not be
possible
Generics (parameterized types) make a solution possible, but not without
breaking legacy programs


<T> boolean equalArrays (T[] a1, T[] a2);
Unlike Java, Scala has had parameterized types from the beginning

Scala does not infer variance types; that’s up to the programmer
19
Multiple parameter lists

def foldLeft [B] (z: B)(op: (B, A) ⇒ B): B








A and B are type parameters
z is of type B
op is a function that takes a B and an A and returns a B
scala> List(1, 2, 3).foldLeft(10.0)(_ + _)
res15: Double = 16.0
The parameter lists allow partial application of the function to create another
function
scala> val fold10 = (_: List[Int]).foldLeft(10.0)(_ + _)
fold10: List[Int] => Double = <function1>
Here we are supplying an indefinite receiver and an indefinite final argument
(underscores)
scala> fold10(List(1, 2, 3))
res26: Double = 16.0
20
Least Upper Bound



The notation B >: A states that B is a superclass of A
All the elements of a List must be of the “same type.”
The “cons” operator, ::, is defined in List as
def :: (x: A): List[A]



Consing a value of type A to a List[A] returns a List[A]
But there is an additional definition,
def :: [B >: A] (x: B): List[B]
If B is a superclass of A, B >: A, then adding a B to a
List[A] will result in a List[B]
21
Sir With-A-Lot

Scala objects are usually built from small pieces



Lots of inheritance, lots of “withing”
The above are buttons, allowing you to control how much is displayed
Anything, such as List above, that inherits from Seq (sequence)
or TraversableLike, will supply the important functions map,
flatMap, filter, and probably some folds
22
Use the source, Luke

Although the documentation is often difficult to understand, the
source code often is not



Scala methods tend to be short
The high-level constructs often make code easier to read
Just as Java has javadoc for producing professional-looking
documentation, Scala has scaladoc

As your code does not have the same constraints as library code, your
scaladoc files will probably not be this complicated
23
The End
24