Feb 4, 2021
Checkpoint 0 will be posted tonight or tomorrow.
Class actually starts at 12:45 (but I'll be around from 12:30 to answer questions).
(assuming you don't already know Scala)
This lecture will focus on syntax.
package edu.buffalo.myapp
import java.io.File
import scala.io._
object MyApp
{
val message: String = "Hello World"
def main(args: Array[String]): Unit = {
println(message)
var stream: Stream = Stream.fromFile(new File("Hello.txt"))
for(line <- stream.getLines){
println(line)
}
}
}
package edu.buffalo.myapp
Package definitions are exactly like Java.
import java.io.File
Import statements bring classes into the namespace, making them available for use.
import scala.io._
'_' acts like a wildcard in scala.
import java.io.{ File, FileInputStream }
Curly braces group class names together as a shorthand when you import multiple classes from the same package.
object MyApp {
Objects in scala are "singletons". They act like Java classes with only static methods. E.g., you could call MyApp.main(...)
class MyClass(name: String, age: Int) {
Classes are defined much like java, except constructor fields are given directly in the class definitions. e.g., you would instantiate this class as new MyClass("Bob", 102).
class MyClass(name: String, age: Int)
extends MyAncestorClass
with MyTrait
with MyOtherTrait {
Inheritence is defined with the extends keyword. Like Java, Scala only allows single inheritance, but you add interfaces and mixins through the with keyword.
trait MyTrait {
...
}
Traits are (almost) like what Java calls interfaces.
Unlike Java, a trait can define methods with implementations.
sealed trait MyTrait {
...
}
Sealed traits can only be extended/with-ed within the same file.
val message: String = "Hello World"
Variables are defined by the val or var keywords. val variables are immutable (like Java's final keyword).
Anywhere a variable is declared, it may be followed by a colon and a type. If omitted, Scala will guess.
Generally prefer to use val wherever you can.
args: Array[String]
Generic types use square brackets ([]). This is like Java's angle brackets (<>)
def main(args: Array[String]): Unit = {
Define functions with the def keyword.
The Colon-Type syntax is used to define the return type. Unit is like Java's void.
The last line of the function will be returned by default, but you can use return to return a value sooner.
Don't forget the =
for(line <- stream.getLines){
...
}
This is scala's iterator syntax (Like Java's for(line : stream.getLines)
stream.getLines.foreach { line =>
...
}
This is another way to write the same thing.
class Foo(bar: String) {
def apply(baz: String): String =
{ bar + " " + baz }
}
The special function apply is used to let a class instance (or object) pretend to be a function.
val myFoo = new Foo("Abe")
val result = myFoo("Lincoln")
println(result)
prints Abe Lincoln
Scala has a robust library of collection types. Collections are usually referenced by their role.
Collections are immutable by default, and already in the namespace (no more import java.util.*).
Mutable collections live in the collections.mutable package if needed.
val seq = Seq[Int](1, 2, 3, 4)
val iseq = IndexedSeq[Int]("Alice", "Bolesław", "Coreline")
val map = Map(
"Cookie" -> "Chocolate Chip",
"Cake" -> "Red Velvet",
"Confection" -> "Gulab Jamun"
)
val opt = if(yes) { Some("A thing") } else { None }
Scala uses round brackets to access collection elements (remember apply?).
println(seq(1))
println(iseq(2))
println(map("Cookie"))
prints 2, Coreline, and Chocolate Chip
val a = (1, "Cookie", "Alice")
val b: (Int, String, String) = (2, "Cake", "Bolesław")
val all: Seq[(Int, String, String)] = Seq(a, b)
Scala also has a "Tuple" type (like Python).
The type is also parenthesized. The elements above would have type (Int, String, String)
Access elements of a tuple by a._1, a._2, and so forth.
For example all(1)._2 is "Cake"
So how about those immutable collections...
Why does a collection need to be mutable?
public int add_one(collection: List<Int>) {
ArrayList<Int> result = new ArrayList<Int>();
for(element : collection){
result.append(element + 1)
}
return result
}
collection.map { x => x+1 }
public int only_big(collection: List<Int>) {
ArrayList<Int> result = new ArrayList<Int>();
for(element : collection){
if(element > 100){
result.append(element)
}
}
return result
}
collection.filter { _ > 100 }
all.filter { x => x._2.equals("Cookie") }
.map { x => x._3 }
Returns Seq("Alice")
public int flatten(collection: List<List<Int>>) {
ArrayList<Int> result = new ArrayList<Int>();
for(nested : collection){
for(element : nested){
result.append(element)
}
}
return result
}
collection.flatten
public int sum(collection: List<Int>) {
int accum = 0;
for(element : collection){
accum += element
}
return accum
}
collection.foldLeft(0) { (accum, element) => accum + element }
collection.foldLeft(0) { _ + _ }
foo match {
case "bar" => println("It was bar")
case "baz" => println("Baz be here")
case x => println("It was actually "+x+" the whole time!")
}
match is like a switch statement in C/C++, or an elif chain in Python...
... but far more powerful.
val longFoo = foo match {
case x:String => x.toLong
case y:Int => y * 100l
case _ => println("giving up!")
}
You can match based on type.
val longFoo = foo match {
case (x, y) => x.toLong
case y:Int => y * 100l
case _ => println("giving up!")
}
You can match based on tuple nesting.
case class Foo(a: String, b: Int)
case class Bar(foo: Foo, baz: String)
Case classes give you a bunch of stuff for "free"
val myFoo = Foo("Abe", 1)
For example, you don't need to use "new" to construct one
and accessors are defined for all of the constructor variables.
val name = bar match {
case Foo(name, id) => name
}
But the big perk is that you can use them in match blocks.
val name = bar match {
case Bar(Foo(name, id), baz) => name
}
... even nested.
Scala uses case classes to make implementing union types easy.
sealed trait MyFooOrBar
case class Foo(a: String, b: Int) extends MyFooOrBar
case class Bar(foo: Foo, baz: String) extends MyFooOrBar
Scala's compiler will warn you if you have a match block for a sealed trait that doesn't match every possible case.
Scala relies on the Scala Build Tool for compilation. It's similar to Maven.
name := "myproject"
version := "0.1"
organization := "edu.buffalo.cse.odin",
Specs2 is my unit testing-framework of choice.
class HelloWorldSpec extends Specification {
"This is a specification for the 'Hello world' string".txt
"The 'Hello world' string should" >> {
"contain 11 characters" >> {
"Hello world" must haveSize(11)
}
"start with 'Hello'" >> {
"Hello world" must startWith("Hello")
}
"end with 'world'" >> {
"Hello world" must endWith("world")
}
}
}
Many IDEs provide SBT integration. See the Scala Getting Started Page and Scala Metals Page for more details.
If you prefer a text editor, check out Bloop.