grammar and invocation | variables and expressions | arithmetic and logic | strings | dates and time | fixed-length arrays | resizable arrays | lists | tuples | dictionaries | functions | execution control | exceptions | concurrency | file handles | files | directories | processes and environment | libraries and namespaces | user-defined types | objects | inheritance and polymorphism | unit tests | debugging and profiling | repl
rust | swift | scala | |
---|---|---|---|
version used |
1.13 | 2.1 | 2.11 |
show version |
$ rustc --version | $ swift --version | $ scala -version |
implicit prologue | none | import Foundation | none; but these libraries always available: java.lang scala as are methods in Predef |
grammar and invocation | |||
rust | swift | scala | |
interpreter |
none | none | $ echo 'println("hello")' > Hello.scala $ scala hello.scala |
compiler | $ cat hello.rs fn main() { println!("Hello, world!"); } $ rustc hello.rs $ ./hello Hello, world! |
$ cat hello.swift print("Hello, World!") $ swift hello.swift $ ./hello Hello, World! |
$ cat hello.scala object Hello { def main(args: Array[String]) { println("Hello, World!") } } $ scalac Hello.scala $ scala Hello |
statement terminator | ; Newlines are not statement terminators; they are permitted wherever spaces or tabs are permitted to separate tokens. |
; or sometimes newline A newline does not terminate a statement when: (1) inside [ ] of an array literal, (2) inside of ( ) parens, (3) after a binary operator, (4) other situations? |
; or sometimes newline A newline does not terminate a statement: (1) inside ( ) or [ ], (2) if the preceding line is not a complete statement, (3) if following token not legal at start of a statement. |
block delimiters |
{ } | { } | { } |
end-of-line comment |
// comment | // comment | // comment |
multiple line comment |
/* comment line /* nested comment */ */ |
/* comment line /* nested comment */ */ |
/* comment line /* nested comment */ */ |
variables and expressions | |||
rust | swift | scala | |
write-once variable |
let pi: f64 = 3.14; | let Pi = 3.14 | // evaluates 1 + p immediately: val n = 1 + p // evaluated first time n is accessed: lazy val n = 1 + p |
modifiable variable | let mut n: i32 = 3; n += 1; |
var n = 3 n += 1 |
var n = 3 n += 1 // evaluates 1 + 2 each time n is used: def n = 1 + 2 |
assignment | let mut i: i32 = 0; // compiler warns if assignment value not used i = 3; |
var i = 0 i = 3 |
|
parallel assignment | // only in variable definition: let (m, n) = (3, 7); |
var (m, n) = (3, 7) | val (m, n) = (3, 7) |
swap | let tmp; tmp = x; x = y; y = tmp; |
(x, y) = (y, x) | |
compound assignment | arithmetic: += -= *= /= %= string: += |
arithmetic: += -= *= /= %= string: += bit: <<= >>= &= |= ^= |
arithmetic: += -= *= /= %= string: none bit: <<= >>= &= |= ^= |
unit type and value | () () |
Void () |
Unit () |
conditional expression | if x > 0 { x } else { -x } | x > 0 ? x : -x | val n = -3 if (n < 0) -n else n |
branch type mismatch | // does not compile: if true { "hello" } else { 3 } |
// syntax error: true ? "hello" : 3 |
// expression has type Any: if (true) { "hello" } else { 3 } |
null |
// Option types only: None |
// option types only: nil |
null |
nullable type | let a: Vec<Option<i32>> = vec![Some(3_i32), None, Some(-4_i32)]; |
val a = List(Some(3), null, Some(-4)) | |
null test | let a = vec![Some(1_i32), None]; match a[0] { None => println!("a[0] is none"), Some(i) => println!("a[0]: {}", i), } // simple comparison also works: if a[1] == None { println!("a[1] is none"); } |
||
coalesce | let a = vec![Some(1_i32), None]; let i: i32 = a[1].unwrap_or(0_i32); |
||
nullif | |||
expression type declaration | 1: Double | ||
arithmetic and logic | |||
rust | swift | scala | |
boolean type |
bool | Bool | Boolean |
true and false |
true false | true false | true false |
falsehoods | false | false | false |
logical operators | && || ! && and || are short-circuit operators. |
&& || ! | && || ! |
relational operators | == != < > <= >= | == != < > <= >= | == != < > <= >= |
min and max | math.min 1 2 math.max 1 2 |
||
integer type | i8 i16 i32 i64 |
Int8 Int16 Int32 (Int) Int64 |
type of integer literals: Int other modular types: Byte Short Long arbitrary precision type: BigInt |
unsigned integer type | u8 u16 u32 u64 |
UInt8 UInt16 UInt32 UInt64 (UInt) |
|
integer literal | -4 // specify size: -4_i32 |
-4 | |
float type | f32 f64 |
Float Double |
type of float literals: Double other types: Float |
arithmetic operators | + - * / % | + - * / % | + - * / % |
add integer and float | let n: i32 = 3; let x = n as f64; let y = x + 0.5; |
3 + 7.0 | |
integer division and remainder |
7 / 3 7 % 3 |
7 / 3 7 % 3 |
7 / 3 7 % 3 |
integer division by zero | runtime error | process sent a SIGILL signal | java.lang.ArithmeticException |
float division |
7_f64 / 3_f64 | Double(7) / 3 | (7: Double) / 3 |
float division by zero | // these are float values but not literals: inf, Nan, or -inf |
// these are float values but not literals: +Inf, NaN, or -Inf |
evaluates to Infinity, NaN, or -Infinity, values which do not have literals |
power | let n = 2_i64.pow(32_u32); let x1 = 3.14_f64.powi(32_i32); let x2 = 3.14_f64.powf(3.5_f64); |
pow(2.0, 32.0) | math.pow(2, 32) |
sqrt |
let x = 2_f64.sqrt(); | sqrt(2.0) | math.sqrt(2) |
sqrt -1 | let x = (-1_f64).sqrt(); // No negative literals and unary negation has lower // precedence that a method, so this is -1: let y = -1_f64.sqrt(); |
// NaN: sqrt(-1) |
math.sqrt(-1) evaluates to NaN, a value which has no literal |
transcendental functions | let x = 0.5_f64; x.exp() x.ln() x.log2() x.log10() x.sin() x.cos() x.tan() x.asin() x.acos() x.atan() x.atan2(3.1_f64) |
exp log log2 log10 sin cos tan asin acos atan atan2 |
math.exp math.log math.sin math.cos math.tan math.asin math.acos math.atan math.atan2 |
transcendental constants | std::f64::consts::PI std::f64::consts::E |
math.Pi math.E |
|
float truncation | 3.77_f64.trunc() 3.77_f64.round() 3.77_f64.floor() 3.77_f64.ceil() |
Int(3.77) Int(round(3.77)) Int(floor(3.77)) Int(ceil(3.77)) |
?? 3.14.round 3.14.floor returns Double 3.14.ceil returns Double |
absolute value and signum |
-7_i32.abs() -7.1_f64.abs() -7_i32.signum() -7.1_f64.signum() |
abs(-7) fabs(-7.77) |
math.abs(-7) math.signum(-7) |
integer overflow | panic | modular arithmetic for all types except BigInt | |
float overflow | evaluates to std::f32::INFINITY or std::f64::INFINITY | evaluates to Infinity, a value which has no literal | |
arbitrary length integer | val n = BigInt(7) val m = BigInt(12) |
||
arbitrary length integer operators | n + m n - m n * m n / m n % m n == m n < m n < m n <= m n >= m |
||
random number uniform int, uniform float, normal float |
use std::rand; let n = rand::random::<uint>() % 100u; let x = rand::random::<f64>(); ?? |
let i = rand() ?? |
import scala.util.Random val rnd = Random rnd.nextInt(100) rnd.nextDouble rnd.nextGaussian |
random seed set, get, restore |
srand(17) | import scala.util.Random val rnd = Random rnd.setSeed(17) none none |
|
bit operators | << >> & | ^ ! | << >> & | ^ ~ | 1 << 4 1 >> 4 1 & 3 1 | 3 1 ^ 3 ~ 1 |
binary, octal, and hex literals | 0b101010 0o52 0x21 |
none 052 0x2a |
|
radix | Integer.toString(42, 7) Integer.parseInt("60", 7) |
||
strings | |||
rust | swift | scala | |
string type |
String string reference: &str |
String | java.lang.String |
string literal |
let s: &str = "don't say \"no\""; let s2: String = "don't say \"no\"".to_string(); |
"hello" | "Hello, World!" """Hello, World!""" |
newline in literal | let s: &str = "first line second line"; // foobar: let s2: &str = "foo\ bar"; |
no | in triple quote literal only |
literal escapes | \0 \\ \t \n \r \" \\ \xhh \uhhhh \Uhhhhhhhh |
\0 \\ \t \n \r \" \' \xhh \uhhhh \Uhhhhhhhh |
\b \f \n \r \t \" \' \uhhhh \o \oo \ooo |
format string | let s = format!("foo {} {} {}", "bar", 7, 3.14_f32); | let n = 3, m = 5 let msg = "\(n) + \(m) is \(n + m)" |
"foo %s %d %.2f".format("bar", 7, 3.1415) val n = 3 val m = 5 val msg = s"$n + $m is ${n + m}" |
concatenate |
let s1: String = "hello".to_string(); let s2: &str = " world"; let s: String = s1 + s2; |
"hello" + " world" | "Hello" + ", " + "World!" |
replicate |
let ch: Character = "-" let hbar = String(count: 80, repeatedValue: ch) |
val hbar = "-" * 80 | |
translate case to upper, to lower |
s.to_uppercase() s.to_lowercase() |
let s = "hello" let s2 = s.uppercaseString let s3 = "HELLO" let s4 = s3.lowercaseString |
"hello".toUpperCase "HELLO".toLowerCase |
capitalize |
"hello".capitalize | ||
trim both sides, left, right |
" hello ".trim | ||
pad on left, on right |
?? "hello".padTo(10, " ").mkString |
||
number to string | let n = String(17) let x = String(17.3) |
"two: " + 2.toString "pi: " + 3.14.toString |
|
string to number | let n = "12".parse::<i32>().unwrap(); let x = ".037".parse::<f64>().unwrap(); // parse() returns an Option type. // unrwap() panics if parse() returns None. |
"17".toInt() // evaluates to nil: "17foo".toInt() // convert to float? |
7 + "12".toInt 73.9 + ".037".toFloat raises NumberFormatException if string doesn't completely parse |
join |
List("do", "re", "mi").mkString(" ") | ||
split |
"do re mi".split(" ") | ||
character type |
Character | Char | |
character literal | 'h' | ||
length |
countElements("hello") | "hello".length | |
index of substring | "hello".indexOf("hell") | ||
extract substring | "hello".substring(0, 4) | ||
extract character | "hello"(0) | ||
chr and ord | 'a'.toInt 97.toChar |
||
dates and time | |||
rust | swift | scala | |
date and time types | extern crate chrono; chrono::DateTime |
java.util.Date | |
current date and time | extern crate chrono; let dt = Local::now(); let dt_utc = UTC::now(); |
import java.util.Date val dt = new Date() |
|
current unix epoch | extern crate chrono; let dt = UTC::now(); let t = dt.timestamp(); |
dt.getTime / 1000 | |
to unix epoch, from unix epoch | extern crate chrono; let t = dt.timestamp(); let dt2 = chrono::NaiveDateTime::from_timestamp(t, 0); |
dt.getTime / 1000 val dt2 = new Date(1451600610 * 1000) |
|
format date | dt.format("%Y-%m-%d %H:%M:%S") | import java.text.SimpleDateFormat val fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") val s = fmt.format(dt) |
|
parse date | import java.text.SimpleDateFormat val fmt = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") val dt = fmt.parse("2011-05-03 17:00:00") |
||
date subtraction | // difference in milliseconds as Long: dt2.getTime - dt.getTime |
||
add duration | // add one day: val dt2 = new Date(dt.getTime + 86400 * 1000) |
||
date parts | import java.util.Date import java.util.Calendar import java.util.GregorianCalendar al cal = new GregorianCalendar cal.setTime(new Date) cal.get(Calendar.YEAR) cal.get(Calendar.MONTH) + 1 cal.get(Calendar.DAY_OF_MONTH) |
||
time parts | import java.util.Date import java.util.Calendar import java.util.GregorianCalendar al cal = new GregorianCalendar cal.setTime(new Date) cal.get(Calendar.HOUR_OF_DAY) cal.get(Calendar.MINUTE) cal.get(Calendar.SECOND) |
||
build broken-down datetime | import java.util.GregorianCalendar val cal = new GregorianCalendar(2015, 12, 31, 23, 59, 59) val dt = cal.getTime |
||
fixed-length arrays | |||
rust | swift | scala | |
literal | let nums = [1i32, 2i32, 3i32]; | val a = Array(1, 2, 3) | |
size | a.size | ||
lookup | nums[0] | val n = a(0) | |
update | let mut nums = [1i32, 2i32, 3i32]; a[2] = 4; |
a(2) = 4 | |
out-of-bounds | compilation error | raises java.lang.ArrayIndexOutOfBounds | |
resizable arrays | |||
rust | swift | scala | |
declare | let mut a = Vec::new(); let mut a2: Vec<i32> = Vec::new(); |
let a: Array<Int> = [] let a2: Int[] = [] |
|
literal | let mut a = vec![1, 2, 3]; | // array is mutable; variable is not: let a = [1, 2, 3] |
import scala.collection.mutable.ArrayBuffer val a = ArrayBuffer(1, 2, 3) |
size | a.len() | a.count | a.length |
lookup | a[0] | a[0] | a(0) |
update | a[0] = 4; | a[0] = 4 | a(0) = 4 |
out-of-bounds | let mut a = vec![1, 2, 3]; // thread panics: let n = a[7]; // returns None: let n = a.get(7); |
raises SIGILL | raises java.lang.ArrayIndexOutOfBoundsException |
element index | a.indexOf(3) | ||
slice | var a = ["a", "b", "c", "d", "e"] // ["c", "d"]: a[2...3] a[2..4] |
val a = ArrayBuffer("a", "b", "c", "d", "e") // ArrayBuffer("c", "d"): a.slice(2, 4) |
|
slice to end | // ArrayBuffer("c", "d", "e"): a.drop(2) |
||
manipulate back | let mut a: Vec<i32> = vec![1, 2, 3]; a.push(4); let n = a.pop(); |
let a = [1, 2, 3] a.append(4) // sets num to 4: let num = a.removeLast() |
// two ways to append: a.append(4) a += 4 // inspect last item: val x = a.last // pop last item: val y = a.remove(a.length - 1) |
manipulate front | let a = [1, 2, 3] a.insert(0, atIndex: 0) // sets num to 0: let num = a.removeAtIndex(0) |
val a = ArrayBuffer(7, 8, 9) a.insert(0, 6) // inspect first element: val x = a.first // pop first element: val y = a.remove(0) |
|
concatenate | let a = [1, 2, 3] a += [4, 5, 6] let a3 = [1, 2, 3] + [4, 5, 6] |
val a1 = ArrayBuffer(1, 2, 3) val a2 = ArrayBuffer(4, 5, 6) // new ArrayBuffer: val a2 = a1 ++ a2 // add elements to a1: a1 ++= a2 |
|
copy | let a: Vec<i32> = vec![1, 2, 3]; let mut a2 = a.clone(); // a[0] does not change: a2[0] = 4; |
let a = [1, 2, 3] let a2 = a // also modifies a[0]: a2[0] = 4 a3 = Array(a) // a[0] remains 4: a3[0] = 5 |
val a = ArrayBuffer(1, 2, 3) val a2 = ArrayBuffer[Int]() a2 ++= a |
iterate over elements | let a: Vec<i32> = vec![1, 2, 3]; for i in a { println!("i: {}", i); } |
for (n <- a) println(n) |
|
iterate over indices and elements | for ((n, i) <- a.zipWithIndex) { println(s"item: $n is at: $i") } |
||
reverse | let mut a: Vec<i32> = vec![1, 2, 3]; a.reverse(); |
let a = [1, 2, 3] let a2 = a.reverse() |
val a2 = a.reverse |
sort | let mut a: Vec<i32> = vec![3, 1, 4, 2]; a.sort(); |
let a = [1, 3, 2, 4] // modifies a in-place and returns it: sort(a) |
val a = ArrayBuffer(3, 1, 4, 2) val a2 = a.sortWith(_ < _) |
dedupe | ArrayBuffer(1, 2, 3, 3).distinct // scala.collection.immutable.Set[Int]: val set = a.toSet |
||
membership | if a.contains(&7) { println!("contains 7"); } |
ArrayBuffer(1, 2, 3).contains(7) | |
intersection | val a1 = ArrayBuffer(1, 2) val a2 = ArrayBuffer(2, 3, 4) // multiset intersection: a1.intersect(a2) |
||
union | val a1 = ArrayBuffer(1, 2) val a2 = ArrayBuffer(2, 3, 4) // multiset union: a1.union(a2) |
||
relative complement | val a1 = ArrayBuffer(1, 2) val a2 = ArrayBuffer(2, 3, 4) // multiset difference: a1.diff(a2) |
||
map | a.map(x => x * x) | ||
filter | a.filter(_ > 2) | ||
fold left | // -6: ArrayBuffer(1, 2, 3).foldLeft(0)(_ - _) |
||
fold right | // -2: ArrayBuffer(1, 2, 3).foldRight(0)(_ - _) |
||
shuffle | val rand = scala.util.Random val a = rand.shuffle(ArrayBuffer(1, 2, 3, 4)) |
||
flatten | val a = ArrayBuffer(ArrayBuffer(1, 2), ArrayBuffer(3, 4)) val a2 = a.flatten |
||
zip | ArrayBuffer(1, 2, 3).zip(ArrayBuffer("a", "b", "c")) | ||
lists | |||
rust | swift | scala | |
literal | // none; use constructor: List(1, 2, 3) |
||
empty list |
Nil List() |
||
empty list test | val list = List(1, 2, 3) list == Nil list.isEmpty |
||
cons |
1 :: List(2, 3) | ||
head |
List(1, 2, 3).head | ||
tail |
List(1, 2, 3).tail | ||
head and tail of empty list | // NoSuchElementException: Nil.head // UnsupportedOperationException: Nil.tail |
||
length |
List(1, 2, 3).length | ||
nth element |
List(1, 2, 3)(0) | ||
element index | // evaluates to 1: List(7, 8, 9).indexOf(8) // evaluates to -1: List(7, 8, 9).indexOf(10) |
||
update | // evaluates to List(1, 4, 3): List(1, 2, 3).updated(1, 4) |
||
concatenate two lists, list of lists |
List(1, 2) ::: List(3, 4) List(1, 2) ++ List(3, 4) List(List(1, 2), List(3, 4)).flatten |
||
last and butlast |
List(1, 2, 3).last List(1, 2, 3).init |
||
take |
List(1, 2, 3).take(2) | ||
drop |
List(1, 2, 3).drop(2) | ||
iterate | List(1, 2, 3).foreach(i => println(i)) for (i <- List.range(1, 11).reverse) println(i) |
||
reverse |
List(1, 2, 3).reverse | ||
sort | List(1, 3, 2, 4).sortWith((x, y) => x < y) List(1, 3, 2, 4).sortWith(_ < _) List(1, 3, 2, 4).sortWith((x, y) => x > y) List(1, 3, 2, 4).sortWith(_ > _) |
||
map | List(1, 2, 3).map(x => 2 * x) List(1, 2, 3).map(2 * _) |
||
filter |
List(1, 2, 3).filter(x => x > 2) | ||
fold from left | List(1, 2, 3).foldLeft(0)(_ + _) List(1, 2, 3).foldLeft(0)((x, y) => x + y) |
||
fold from right |
List(1, 2, 3).foldRight(0)(_ - _) | ||
membership |
List(1, 2, 3).contains(3) | ||
universal test |
List(1, 2, 3).forall(_ > 2) | ||
existential test |
List(1, 2, 3).exists(_ > 2) | ||
zip lists | List(1, 2, 3).zip(List("a", "b", "c")) | ||
tuples | |||
rust | swift | scala | |
literal |
(1, "hello", true) | (1, "hello", true) | (1, "hello", true) |
type |
let tup: (i32, &str, bool) = (1, "hello", true); | let tup: (Int, String, Bool) = (1, "hello", true) | val tup: (Int, String, Boolean) = (7, "foo", true) |
lookup | let tup = (1, "hello", true); let n: i32 = tup.0 |
let tup = (1, "hello", true) let n: Int = tup.0 |
val tup = (1, "hello", true) val n: Int = tup._1 |
deconstruct | let tup = (1, "hello", true); let (n, s, b) = tup; // use underscores for unneeded elements: let (n, _, _) = tup; |
let tup = (1, "hello", true) let (n, s, b) = tup // use underscores for unneeded elements: let (n, _, _) = tup |
val tup = (1, "hello", true) tup match { case (_, s, _) => println(s) case _ => throw new Exception("bad tuple") } |
dictionaries | |||
rust | swift | scala | |
declare | let mut dict = std::collections::HashMap::new(); | let dict = Dictionary<String, Int>() | import scala.collection.mutable val dict = mutable.Map.empty[String, Int] |
literal | // no dict literal let mut dict = std::collections::HashMap::new(); dict.insert("t", 1); dict.insert("f", 0); |
let dict = ["t": 1, "f": 0] | // scala.collection.immutable.Map[String,Int]: val dict = Map("t" -> 1, "f" -> 0) |
size |
dict.len() | dict.count | dict.size |
lookup | dict["t"] | dict("f") // returns Option[Int]: dict.get("f") |
|
update | dict["t"] = 2 | // mutable.Map only: dict("t") = 2 |
|
out-of-bounds behavior |
returns nil | raises java.util.NoSuchElementException | |
is key present | if dict["y"] { println("key found") } else { println("no such key") } |
dict.exists(kv => kv._1 == "t") | |
delete | dict.removeValueForKey("t") | // mutable.Map only: dict.delete("t") |
|
iterate | for (k, v) in dict { println("\(k): \(v)") } |
for (kv <- dict) { println(kv._1) println(kv._2) } |
|
keys and values as arrays | // dict.keys and dict.values are iterable: Array(dict.keys) Array(dict.values) |
// Iterable[String]: dict.keys // Array[String]: dict.keys.toArray dict.values dict.values.toArray |
|
functions | |||
rust | swift | scala | |
define function | fn add(x: f64, y: f64) -> f64 { x + y } |
func add(n: Int, m: Int) -> Int { return n + m } |
// argument types must be declared: def average(a: Double, b: Double) = (a + b) / 2.0 // return value type must be declared if // function is recursive: def factorial(n: Int): Int = if (n < 1) 1 else n * factorial(n - 1) |
invoke function | add(3.7, 2.8) | add(3, 7) | // 3.0: average(1, 2 + 3) // 4.5: average(1, 2) + 3 // parens can be omitted when a function // takes no arguments; by convention parens // are omitted when the function has no // side effects |
define function with block body | // braces must be used if body // not an expression: def print_numbers() = { println("one") println("two") } |
||
nest function | fn add_one(x: f64) -> f64 { fn add(x1: f64, y1: f64) -> f64 { x1 + y1 } add(x, 1.0) } |
func add_one(n: Int) -> Int { func add(a: Int, b: Int) -> Int { return a + b } return add(1, n) } |
|
named parameter | func my_log(#exp: Double, #base: Double) -> Double { return log(exp) / log(base) } // expose different parameter names: func my_log(exp e: Double, base b: Double) -> Double { return log(e) / log(b) } my_log(exp: 8, base: 2) |
def subtract(m: Int, s: Int) = m - s subtract(s = 3, m = 7) |
|
named parameter default value | func incr(n: Int, amount: Int = 1) -> Int { return n + amount } // 4: incr(3) // 5: incr(3, amount: 2) |
def logarithm(x: Double, base: Double = math.exp(1)) = math.log(x) / math.log(base) logarithm(2.718) logarithm(10, base = 2) |
|
variable number of arguments | func concat(strings: String...) -> String { var retval = "" for string in strings { retval += string } return retval } |
||
overload function | func add(a: String, b: String) -> String { return a + b } |
||
return value | return arg; otherwise last expression evaluated and not followed by semicolon; otherwise unit () | return arg | if function body is preceded by = // the return value is the// return arg or last expression evaluated. If function body not preceded by = the return value is Unit. |
multiple return values | func divmod(dividend: Int, divisor: Int) -> (Int, Int) { return (dividend / divisor, dividend % divisor) } |
||
recursive function | def range(a:Int, b:Int): List[Int] = if (a > b) List() else a :: range(a + 1, b) |
||
anonymous function | let add_one = {(n: Int) -> Int in n + 1} | (x: Double, y: Double) => (x + y) / 2.0 | |
invoke anonymous function | add_one(2) | ||
function as value | func add(n: Int, m: Int) -> Int { return n + m } let f = add |
||
infix operator in prefix position | none | ||
function in infix position | unary methods can be used as binary operators | ||
currying | def plus(x: Int)(y: Int) = x + y plus(3)(7) def plus2 = plus(2) plus2(7) |
||
lazy evaluation | def arg1(x: => Int, y: => Int): Int = x arg1(7, 1 / 0) |
||
execution control | |||
rust | swift | scala | |
if | let signum: i32; if i > 0 { signum = 1 } else if i == 0 { signum = 0 } else { signum = -1 } |
var signum: Int if i > 0 { signum = 1 } else if i == 0 { signum = 0 } else { signum = -1 } |
if (x > 0) println("pos") else if (x < 0) println("neg") else println("zero") |
while | let mut i: i32 = 0; while i < 10 { i += 1 } |
var i = 0 while i < 10 { i += 1 } |
var i = 0 while (i<10) { printf("%d\n", i) i = i+1 } |
for | let mut n: i32 = 1; for i in range(1i, 11i) { n *= i; } |
var n = 1 for var i = 1; i <= 10; i++ { n *= i } |
for (i <- 1 to 10) println(i) |
infinite loop | loop { } |
while (true) { } |
|
break and continue |
break continue | import scala.util.control.Breaks.break for (i <- 1 to 10) if (i > 5) break else println(i) // there is no continue statement |
|
exceptions | |||
rust | swift | scala | |
raise error | panic!("bam!"); | throw new Exception("bam!") | |
handle error | // does not catch all panics: let result = std::panic::catch_unwind(|| { panic!("bam!"); }); if result.is_ok() { println!("no panic"); } |
import java.lang._ val x = try { 1 / 0 } catch { case e: ArithmeticException => 0 } |
|
standard exceptions | defined in java.lang: Throwable Error Exception IOException EOFException FileNotFoundException MalformedURLException UnknownHostException ClassNotFoundException CloneNotSupportedException RuntimeException ArithmeticException ClassCastException IllegalArgumentException IllegalStateException IndexOutOfBoundsException NoSuchElementException NullPointerException Error, RuntimeException, and subclasses theoreof are normally unrecoverable |
||
assert | assert!(1 == 0); | assert(1 == 0) | |
concurrency | |||
rust | swift | scala | |
file handles | |||
rust | swift | scala | |
standard file handles | use std::io; io::stdin io::stdout io::stderr |
let stdin = NSFileHandle.fileHandleWithStandardInput() let stdout = NSFileHandle.fileHandleWithStandardOutput() let stderr NSFileHandle.fileHandleWithStandardError() |
System.in System.out System.err |
read line from stdin | use std::io; let s = io::stdin().read_line().ok().expect("Failed to read line"); |
none | import scala.io.StdIn.readLine // newline is removed: val s = readLine() |
write line to stdout | println!("Hello, World!"); // argument of println! must be a literal. // To print a variable: let s = "Hello, World!"; println!("{}", s); |
print("Hello, World!") | println("lorem ipsum") |
write formatted string to stdout | let s = "Spain" let i = 17 let x = 3.1415 let fmtx = NSString(format: "%.2f", x) print("\(s) \(i) \(fmtx)") |
printf("%s %d %.2f", "Spain", 17, 3.1415) | |
open file for reading | import scala.io.Source val path = "/etc/hosts" val f = Source.fromFile(path) |
import scala.io.Source val path = "/etc/hosts" val f = Source.fromFile(path) |
|
open file for writing | let path = "/tmp/test" NSFileManager().copyItemAtPath( "/dev/null", toPath: path, error: nil) let f = NSFileHandle( forWritingAtPath: path) |
||
open file for appending | let f = NSFileHandle( forWritingAtPath: "/tmp/err.log") f.seekToEndOfFile() |
||
close file | f.closeFile() | import scala.io.Source f.close |
|
close file implicitly | class Defer { var fun: ()->() init(fun: ()->()) { self.fun = fun } deinit { fun() } } var defer = Defer({()->() in f.closeFile()}) |
||
read line | none | import scala.io.Source val src = Source.fromFile("/etc/passwd") for (line <- src.getLines) print(line) |
|
iterate over file by line | none | ||
read file into array of strings | none | ||
read file into string | let data = f.readDataToEndOfFile() let s = NSString( data: data, encoding: NSUTF8StringEncoding) |
||
write string | f.writeData("Hello, World!".dataUsingEncoding( NSUTF8StringEncoding)) |
||
write line | f.writeData("Hello, World!\n".dataUsingEncoding( NSUTF8StringEncoding)) |
val out = new java.io.FileWriter("/tmp/test-scala") out.write("hello out\n") out.close |
|
flush file handle | f.synchronizeFile() | ||
get and set filehandle position | let pos = f.offsetInFile f.seekToFileOffset(0) |
||
files | |||
rust | swift | scala | |
file test, regular file test | import java.io.File val f = new File("/etc/hosts") f.exists f.isFile |
||
file size | import java.io.File val f = new File("/etc/hosts") f.length |
||
is file readable, writable, executable | import java.io.File val f = new File("/etc/hosts") f.canRead f.canWrite f.canExecute |
||
set file permissions | mport java.io.File val f = new File("/tmp/foo") // sets owner perms; to turn perms off // set arg to false: f.setReadable(true) f.setWritable(true) f.setExecutable(true) // if 2nd arg is false, perms are // for owner, group, and other: f.setReadable(true, false) f.setWritable(true, false) f.setExecutable(true, false) |
||
copy file, remove file, rename file | import java.nio.file.Files import java.nio.file.Paths val path = Paths.get("/tmp/foo") // possible java.nio.file.FileAlreadyExistsException: Files.copy(path, Paths.get("/tmp/bar")) Files.deleteIfExists(path) // possible java.nio.file.NoSuchFileException: Files.delete(path) Files.move(Paths.get("/tmp/bar", path) |
||
create symlink, symlink test, readlink | import java.nio.file.Files import java.nio.file.Paths val target = Paths.get("/etc/hosts") val path = Paths.get("/tmp/hosts") // Possible java.nio.file.FileAlreadyExistsException: Files.createSymbolicLink(path, target) Files.isSymbolicLink(path) Files.readSymbolicLink(path) |
||
generate unused file name | import java.nio.file.Files val path = Files.createTempFile("foo", ".txt") |
||
directories | |||
rust | swift | scala | |
build pathname | import java.io.File val root = File.listRoots()(0) val hosts = new File(new File(root, "etc"), "hosts") val path = hosts.getPath |
||
dirname and basename | import java.io.File val f = new File("/etc/hosts") val dirn = f.getParent val basen = f.getName |
||
iterate over directory by file | import java.io.File val dir = new File("/etc") // Array[String]: dir.list // Array[java.io.File]: dir.listFiles |
||
make directory | import java.io.File val dir = new File("/tmp/foo/dir") dir.mkdirs |
||
remove empty directory | import java.io.File val dir = new File("/tmp/foodir") dir.delete |
||
remove directory and contents | // libraryDependencies += "commons-io" % "commons-io" % "2.4" import org.apache.commons.io.FileUtils import java.io.File FileUtils.deleteDirectory(new File("/tmp/foo")) |
||
directory test | import java.io.File val f = new File("/etc") f.isDirectory |
||
temporary directory | import java.nio.file.Files val dir = Files.createTempDirectory(null) // path as string: dir.toString // arrange for directory to be deleted: dir.toFile.deleteOnExit |
||
processes and environment | |||
rust | swift | scala | |
command line arguments | object Test { def main(args: Array[String]) { for (arg <- args) println(arg) } } |
||
program name |
A scala program is run as scala CLASS [ARG …] The VM then searches CLASSPATH for CLASS. CLASS is the nearest analog to the program name and can be determined statically. |
||
getopt | // built.sbt: // // libraryDependencies += "com.github.scopt" %% "scopt" % "3.3.0" case class Config( foo: Int = 0, bar: String = "" ) val parser = new scopt.OptionParser[Config]("scopt") { opt[Int]('f', "foo") action { (x, c) => c.copy(foo = x) } text("foo is integer") opt[String]('b', "bar") action{ (x, c) => c.copy(bar = x) } text("bar is string") } parser.parse(args, Config()) match { case Some(config) => println(config.foo) println(config.bar) case None => // bad args; usage was displayed } |
||
get and set environment variable |
// java.util.NoSuchElementException if not defined: sys.env("HOME") // returns Option[String]: sys.env.get("HOME") // Environment variables are read only, but new values can be // set when creating child processes. |
||
get pid, parent pid |
// no portable technique | ||
get user id and name | System.getProperty("user.name") // no property for uid |
||
exit |
System.exit(1) | ||
set signal handler |
import sun.misc._ Signal.handle(new Signal("INT"), new SignalHandler() { val start = System.nanoTime() def handle(sig: Signal) { val end = System.nanoTime() println(s"\n${(end - start) / 1e9f} seconds") System.exit(0) } }) |
||
external command |
import scala.sys.process._ val exitCode = "ls /tmp".! |
||
escaped external command |
import scala.sys.process._ import java.io.File // if args contain spaces, use List(): val exitCode = List("touch", "/tmp/some file").! // there are operators for shell &&, ||, and |: (( "ls /tmp" #&& "ls /etc") #| "grep ssh").! // redirection example: ("ls" #> new File("/tmp/ls.out")).! |
||
backticks |
import scala.sys.process._ val s = "ls".!! |
||
libraries and namespaces | |||
rust | swift | scala | |
define namespace | package foo { class A class B } // Alternate syntax; must be first statement in file; // declares entire file to be in namespace package foo |
||
define child namespace | package foo.bar { class A } // Alternate nested syntax: package foo { package bar { class A } } // Alternate syntax; must be first statement in file: package foo.bar |
||
reference identifier in another file | // no import needed if identifier is fully qualified: val a = new foo.bar.A |
||
import definitions | import foo.A val a = new A // imports A and B: import foo.{A, B} // Import statements can appear after or inside class definitions, // or inside methods. |
||
import all definitions in namespace | import foo._ val a = new A val b = new B |
||
import namespace | import foo.bar val a = new bar.A val b = new bar.B |
||
shadow avoidance | import foo.bar.{A => LetterA} val a = new LetterA |
||
library path environment variable | $ cat src/foo/bar/A.scala package foo.bar object A { def main(args: Array[String]) { println("Hello, World!") } } $ scalac src/foo/bar/A.scala $ dir=$(pwd) $ cd / $ CLASSPATH=$dir scala foo.bar.A The default CLASSPATH is the current directory. Directories are separated by colons : on Unix and semicolons ; on Windows. Jar files can also be put in the CLASSPATH. |
||
create package | $ cat src/foo/bar/A.scala package foo.bar object A { def main(args: Array[String]) { println("Hello, World!") } } $ mkdir target $ scalac -d target src/foo/bar/A.scala $ find target -name '*.class' | xargs jar cf App.jar $ CLASSPATH=App.jar scala foo.bar.A |
||
inspect package | $ jar tf App.jar | ||
install package | $ cat Cargo.toml [package] name = "main" version = "0.1.0" authors = ["Bob <bob@foo.com>"] [dependencies] chrono = "0.2.25" $ cat src/main.rs extern crate chrono; fn main() { let t = chrono::UTC::now(); println!("t: {}", t.format("%Y-%m-%d")); } $ cargo build |
$ cat build.sbt libraryDependencies += "commons-io" % "commons-io" % "2.4" $ sbt package |
|
list installed packages | $ find ~/.ivy2/cache -name '*.jar' | ||
user-defined types | |||
rust | swift | scala | |
typealias CustomerId = Int var customer_id: CustomerId = 3 |
type Name = String | ||
sum type | enum DayOfWeek { Mon, Tue, Wed, Thu, Fri, Sat, Sun } let dow: DayOfWeek = Mon; |
enum DayOfWeek { case Mon, Tue, Wed, Thu, Fri, Sat, Sun } let dow = DayOfWeek.Tue |
abstract class Color case object Red extends Color case object Blue extends Color case object Green extends Color val col = Red // this won't compile: col < Green |
tuple product type with one field | class SpecialInt(x: Int) val n = new SpecialInt(7) |
||
tuple product type with two fields | class IntPair(a: Int, b: Int) val p = new IntPair(7, 11) |
||
record product type | struct MedalCount { var country: String var gold: Int, silver: Int, bronze: Int } |
case class Customer( id: Int, name: String, address: String ) |
|
record product type literal | var spain = MedalCount( country: "Spain", gold: 3, silver: 2, bronze: 1 ) |
Customer(7,"John","Topeka, KS") Customer(id=7, name="John", address="Topeka, KS") |
|
product type member access | let france_total = france.gold + france.silver + france.bronze | ||
product type member assignment | var france: MedalCount france.country = "France" france.gold = 7 france.silver = 6 france.bronze = 5 |
||
generic type | class Twosome[A, B](a: A, b: B) val p = new Twosome("pi", 3.14) |
||
recursive type | abstract class BinaryTree case class Tree(left: BinaryTree, right: BinaryTree) extends BinaryTree case class Leaf(x: Int) extends BinaryTree |
||
pattern match sum type | let msg = match col { Red => "red", Blue => "blue", Green => "green", }; |
val c:Color = Red; c match { case Red => "red"; case Green => "green"; case Blue => "blue" } |
|
pattern match product type | |||
pattern match guard | match { case i: Int if i < 0 => - i; case i: Int => i } | ||
pattern match catchall | let msg = match col { Red => "red", _ => "not red", }; |
val c : Color = Green c match { case Red => "red"; case _ => "not red" } |
|
objects | |||
rust | swift | scala | |
define class | // Constructor takes optional param of type Int. // // Precede param name by val or var // to implicitly define an instance variable class Counter(n: Int = 0) { // Executes when object is created. // // java.lang.IllegalArgumentException if false. require(n >= 0) // Instance variables public by default private var _n = n // Getter: def value = _n // Setter: def value_=(n: Int) { _n = n } // Object-mutating method: def incr { _n += 1 } } |
||
create object | val c = new Counter val c2 = new Counter(1) |
||
invoke method | c.incr() c.value = c.value + 1 |
||
define class variable and method | // Define singleton object outside of class body: object Counter { // Class variables can be declared private; Counter constructor // and instance methods stil have access: var instances = 0 def incrInstances { instances += 1 } } |
||
invoke class method | Counter.incrInstances | ||
inheritance and polymorphism | |||
rust | swift | scala | |
subclass | class Base { println("instantiating Base") def name = { "Base" } } class Derived extends Base { println("instantiating Derived after Base") // Compilation error if override omitted or // if method with same name not defined in base class. override def name = { "Derived" } } |
||
abstract base class | abstract class Base { // compilation error if derived class does not define name: def name: String } |
||
mixin | |||
unit test | |||
rust | swift | scala | |
test class | import org.scalatest.FunSuite class FooSuite extends FunSuite { test("a simple test") { assert(0 == 0, "zero not equal to itself") } } |
||
run all tests, run test suite | $ cat build.sbt libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.0-SNAP13" $ sbt test $ sbt > testOnly FooSuite |
||
exception assertion | intercept[IndexOutOfBoundsException] { "val a = List(1, 2, 3) "val n = a(4) } |
||
setup | import org.scalatest.FunSuite import org.scalatest.BeforeAndAfter class FooSuite extends FunSuite with BeforeAndAfter { before { print("before called\n") } test("zero equals self") { assert(0 == 0) } } |
||
teardown | import org.scalatest.FunSuite import org.scalatest.BeforeAndAfter class FooSuite extends FunSuite with BeforeAndAfter { test("zero equals self") { assert(0 == 0) } after { print("after called\n"); } } |
||
debugging and profiling | |||
rust | swift | scala | |
lint | $ brew install scalastyle $ scalastyle Hello.scala |
||
run debugger | Scala IDE for Eclipse has a debugger | ||
profile code | Runs app under a sampling profiler. Profiling info is written to stdout. HotSpot VM only. $ JAVA_OPTS=-Xprof scala SomeApp |
||
memory tool | $ JAVA_OPTS=-XX:+PrintGCDetails scala SomeApp | ||
repl | |||
rust | swift | scala | |
invoke repl | $ swift | $ scala | |
previous values | $R0, $R1, … | res0, res1, … | |
help | :help | :help | |
quit | :quit | :quit | |
inspect type | repl displays the type of any expression entered | ||
________________________________________________________________ | _________________________________________________________________ | _________________________________________________________________ |
version used
The version used for examples in this sheet.
show version
How to get the installed version.
implicit prologue
Boilerplate which is assumed to be present by examples in this sheet.
Grammar and Invocation
interpreter
How to run the interpreter on a file of source code.
scala:
Scala can be run "Perl style" like this:
scala foo.scala
or "Java style" like this:
scala Foo
When the code is run "Java style", the code to be executed must be in the main method of an object with the same name as the file. When the code is run "Perl style" the statements o be executed should be at the top level outside of any object, class, or method.
To use scala as a shebang, it is necessary to terminate the shell script portion of the script with !#
#!/bin/sh
exec scala $0 $@
!#
println("hello world")
compiler
How to run the compiler.
statement terminator
scala:
Scala infers the existence of a semicolon at the end of a newline terminated line if none of the following conditions hold:
- the line ends with a infix operator, including a period
- the following line begins with a word that is not legal at the start of a statement
- the line ends inside parens or square brackets, neither of which can contain multiple statements
block delimiters
How blocks of statements are delimited.
end-of-line comment
The syntax for a comment which goes to the end of the line.
multiple line comment
The syntax for a comment which beginning and ending delimiters which can span multiple lines.
Variables and Expressions
let ... in ...
How to define local variables.
scala:
Blocks can be used in Scala exclusively to define scope. Furthermore blocks are expressions and evaluate to their last statement.
Arithmetic and Logic
integer types
The most commonly used numeric types.
scala:
Arithmetic operators can be used on values of type Char, which then behaves as a 16 bit unsigned integer. Integer literals are of type Int unless suffixed with L:
scala> 9223372036854775807L
res24: Long = 9223372036854775807
scala> 9223372036854775807
<console>:1: error: integer number too large
integer overflow
What happens when expression evaluates to an integer that is larger than what can be stored.
scala:
The largest integers are available in the constants Int.MaxValue and Long.MaxValue.
random number
How to generate a uniformly distributed random integer; how to generate a uniformly distributed float; how to generate a normally distributed float.
scala:
One can also use java.util.Random, which does not have to be imported.
random seed
How to set a random seed. How to get and restore the state of a random number generator.
scala:
It looks like Scala 2.10 has modified the Random constructor so that it will accept an Int or Long as a seed argument.
Strings
string type
The types for strings and characters.
string literal
The syntax for a string literal.
newline in literal
literal escapes
scala:
Unicode escapes might not work when scala is installed on a Mac because the encoding is set by default to MacRoman:
scala> System.getProperty("file.encoding")
res0: java.lang.String = MacRoman
This can be fixed by passing the following flag to java in the scala shell script:
-Dfile.encoding=UTF-8
format string
concatenate
How to concatenate strings.
replicate
translate case
How to convert a string to uppercase; how to convert a string to lowercase; how to capitalize the first character.
capitalize
trim
pad
number to string
string to number
How to parse numeric types from string; how to convert numeric types to strings.
scala:
The + operator will convert a numeric type to a string if the other operand is a string. Hence the following works:
"value: " + 8
join
split
character type
character literal
length
How to get the length of a string.
index of substring
How to get the index of a substring.
extract substring
How to extract a substring.
extract character
How to get the character at a specified index of a string.
The syntax for a character literal.
chr and ord
How to convert a character to its ASCII code or Unicode point; how to convert an ASCII code or Unicode point to a character.
Dates and Time
Fixed-Length Arrays
Resizable Arrays
Lists
list literal
list element element
list head
list-tail
Supports List.tl (with a warning) to be compatible with OCaml.
Tuples
literal
The syntax for a tuple literal.
type
How to declare a variable with a tuple type.
lookup
How to lookup an element in a tuple.
deconstruct
How to extract all the elements in a tuple.
Dictionaries
Functions
function
How to define a function.
scala
Recursive functions must have their return type declared because the Scala compiler cannot infer it.
lambda
How to define an anonymous function.
piecewise defined function
How to define a function with multiple equations and matching on the arguments.
recursive function
How to define a recursive function.
mutually recursive functions
How to define two functions which call each other. Mutual recursion can be eliminated by inlining the second function inside the first function. The first function is then recursive and can be defined independently of the second function.
named parameter
How to define and invoke a function with named parameters.
named parameter default value
How to make named parameters optional by providing a default value in the definition.
infix operator in prefix position
How to invoke an infix operator in prefix position.
function in infix position
How to invoke a function in infix position.
currying
How to create a curried function by providing values for some of the arguments of a function.
scala:
Functions can only be curried if they are defined with special syntax. Functions defined with this syntax must be invoked with a pair of parens for each argument.
function composition operator
An operator which takes two functions as arguments and returns a function constructed from them by composition.
lazy evaluation
How to evaluate the arguments to a function in a lazy manner.
Lazy evaluation is also called call-by-name.
scala:
Functions can be defined to evaluate their arguments lazily by putting a => operator between the colon and the type of the parameter in the function signature.
We can define arg1 so that the first argument is strict and the second argument is lazy:
def arg1(x: Int, y: => Int): Int = x
arg1(7, 1 / 0)
strict evaluation
How to evaluate arguments before they are passed to a function.
Strict evaluation is also called call by-value.
Execution Control
if
The if statement.
while
The while loop.
for
infinite loop
An infinite loop.
break and continue
Statements for exiting a loop or ending an iteration of a loop.
Exceptions
raise error
How to raise an error.
handle error
How to handle an error.
Concurrency
Filehandles
Files
Directories
Processes and Environment
Libraries and Namespaces
namespace example
namespaces
file name restrictions
import
namespace creation
namespace alias
namespace separator
subnamespace
inspect namespace
User-Defined Types
type synonym
sum type
generic type
recursive type
Objects
Inheritance and Polymorphism
REPL
repl
repl limitations
repl last value
help
inspect type
load source file
search path
set search path on command line
Rust
The Rust Reference
The Rust Standard Library
Scala
The Scala Language Specification: Version 2.9 (pdf)
Scala API Docs
Swift
As of June 2014, to use Swift one must download and install a beta version of Xcode 6, then:
$ sudo xcode-select -s /Applications/Xcode6-Beta.app/Contents/Developer/
$ xcrun swift