This lesson covers:
The first few weeks will cover basic syntax and concepts. Then we’ll start to open it up with more exercises.
Some examples will be given as if written in the interpreter, others as if written in a source file.
Having an interpreter available makes it easy to explore a problem space.
Scala is not just a nicer Java. You should learn it with a fresh mind, you will get more out of these classes.
Start the included sbt console.
$ sbt console [...] Welcome to Scala version 2.8.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20). Type in expressions to have them evaluated. Type :help for more information. scala>
scala> 1 + 1 res0: Int = 2
res0 is an automatically created value name given by the interpreter to the result of your expression. It has the type Int and contains the Integer 2.
(Almost) everything in Scala is an expression.
You can give the result of an expression a name.
scala> val two = 1 + 1 two: Int = 2
You cannot change the binding to a val.
If you need to change the binding, you can use a var instead
scala> var name = "steve" name: java.lang.String = steve scala> name = "marius" name: java.lang.String = marius
You can create functions with def.
scala> def addOne(m: Int): Int = m + 1 addOne: (m: Int)Int
In Scala, you need to specify the type signature for function parameters. The interpreter happily repeats the type signature back to you.
scala> val three = addOne(2) three: Int = 3
You can leave off parens on functions with no arguments
scala> def three() = 1 + 2 three: ()Int scala> three() res2: Int = 3 scala> three res3: Int = 3
You can create anonymous functions.
scala> (x: Int) => x + 1 res2: (Int) => Int = <function1>
This function adds 1 to an Int named x.
scala> res2(1) res3: Int = 2
You can pass anonymous functions around or save them into vals.
scala> val addOne = (x: Int) => x + 1 addOne: (Int) => Int = <function1> scala> addOne(1) res4: Int = 2
If your function is made up of many expressions, you can use {} to give yourself some breathing room.
def timesTwo(i: Int): Int = {
println("hello world")
i * 2
}
This is also true of an anonymous function
scala> { i: Int =>
println("hello world")
i * 2
}
res0: (Int) => Int = <function1>
You will see this syntax often used when passing an anonymous function as an argument.
You can partially apply a function with an underscore, which gives you another function.
scala> def adder(m: Int, n: Int) = m + n adder: (m: Int,n: Int)Int
scala> val add2 = adder(2, _:Int) add2: (Int) => Int = <function1> scala> add2(3) res50: Int = 5
You can partially apply any argument in the argument list, not just the last one.
Sometimes it makes sense to let people apply some arguments to your function now and others later.
Here’s an example of a function that lets you build multipliers of two numbers together. At one call site, you’ll decide which is the multiplier and at a later call site, you’ll choose a multipicand.
scala> def multiply(m: Int)(n: Int): Int = m * n multiply: (m: Int)(n: Int)Int
You can call it directly with both arguments.
scala> multiply(2)(3) res0: Int = 6
You can fill in the first parameter and partially applying the second.
scala> val timesTwo = multiply(2)(_) timesTwo: (Int) => Int = <function1> scala> timesTwo(3) res1: Int = 6
This sometimes lead to crazy pieces of code.
multiplyThenFilter { m: Int =>
m * 2
} { n: Int =>
n < 5
}
I promise you get used to this over time.
You can take any function of multiple arguments and curry it. Let’s try with our earlier
adder
scala> (adder(_, _)).curried res1: (Int) => (Int) => Int = <function1>
There is a special syntax for methods that can take parameters of a repeated type.
def capitalizeAll(args: String*) = {
args.map { arg =>
arg.capitalize
}
}
scala> class Calculator {
| val brand: String = "HP"
| def add(m: Int, n: Int): Int = m + n
| }
defined class Calculator
scala> val calc = new Calculator
calc: Calculator = Calculator@e75a11
scala> calc.add(1, 2)
res1: Int = 3
scala> calc.brand
res2: String = "HP"
Contained are examples are defining methods with def and fields with val. methods are just functions that can access the state of the class.
Constructors aren’t special methods, they are the code outside of method definitions in your class. Let’s extend our Calculator example to take a constructor argument and use it to initialize internal state.
class Calculator(brand: String) {
/**
* A constructor.
*/
val color: String = if (brand == "TI") {
"blue"
} else if (brand == "HP") {
"black"
} else {
"white"
}
// An instance method.
def add(m: Int, n: Int): Int = m + n
}
Note the two different styles of comments.
Our BasicCalculator example gave an example of how Scala is expression-oriented. The value color was bound based on an if/else expression. Scala is highly expression-oriented, most things are expressions rather than statements.
class ScientificCalculator(brand: String) extends Calculator(brand) {
def log(m: Double, base: Double) = math.log(m) / math.log(base)
}
class EvenMoreScientificCalculator(brand: String) extends ScientificCalculator(brand) {
def log(m: Int) = log(m, math.exp(1))
}
traits are collections of fields and behaviors that you can extend or mixin to your classes.
trait Car {
val brand: String
}
class BMW extends Car {
val brand = "BMW"
}
Earlier, you saw that we defined a function that took an Int which is a type of Number. Functions can also be generic and work on any type. When that occurs, you’ll see a
type parameterintroduced with the square bracket syntax:
You can introduce as many type parameters. Here’s an example of a Cache of generic Keys and Values.
trait Cache[K, V] {
def get(key: K): V
def put(key: K, value: V)
def delete(key: K)
}
Methods can also have type parameters introduced.
def remove[K](key: K)