Created
August 3, 2011 16:36
-
-
Save mrdanadams/1123089 to your computer and use it in GitHub Desktop.
Scala Language Examples
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* var vs val and defining variables | |
*/ | |
var changeMe = 1 // variable assignment | |
val dontChangeMe = 2 // constant assignment | |
changeMe = 3 | |
assert(changeMe == 3) | |
// this causes the error: reassignment to val | |
//dontChangeMe = 4 | |
// you can still change the contents of values that are objects | |
val constArray = Array(1) | |
assert(constArray(0) == 1) | |
constArray.update(0, 10) | |
assert(constArray(0) == 10) | |
var s = "foo" // type is implied | |
var s2:String = "3" // full version | |
assert(s.getClass.getSimpleName == "String") | |
// this would cause an error as it's statically typed | |
// s2 = 3 | |
assert(s2 != 3) | |
assert(s2.toInt == 3) | |
/* | |
* Strings | |
*/ | |
assert("foo".isInstanceOf[String]) | |
assert('f'.isInstanceOf[Char]) | |
// multiline string | |
assert(""" | |
this | |
is \my | |
string | |
""" == "\nthis\nis \\my\nstring\n") | |
// string interpolation as in ${...} or #{...} is not supported. it's +..+ in scala. | |
assert("a"+1+"c" == "a1c") // recommended way in scala. shorter and concise. | |
assert("%d+%d=%d".format(1,2,3) == "1+2=3") | |
// see StringBuilder for enhanced string manipulation | |
/* | |
* Operators are just methods | |
*/ | |
// note: class not ideal. just for illustration. | |
class SimpleValue(private var _value:Int) { | |
def value:Int = _value // getter | |
// def value_=(v:Int) { value = v } // setter (note the _) | |
def +(v:Int) = new SimpleValue(value + v) | |
def times(n:Int) = new SimpleValue(value * n) // methods are operators too | |
def unary_! = new SimpleValue(-value) | |
def -:(v:Int) = new SimpleValue(value - v) | |
} | |
var v = new SimpleValue(1) | |
assert(v.value == 1) | |
assert((v.+(3)).value == 4) | |
var v2 = v + 3 | |
assert(v2.value == 4) | |
v2 = v2 times 2 | |
assert(v2.value == 8) | |
v2 = !v | |
assert(v2.value == -1) | |
v2 = 8 -: v | |
assert(v2.value == -7) | |
/* | |
* Functions | |
*/ | |
// shows different ways to define functions | |
def one = 1 // can you guess what this does? | |
def self(a:Int) = a // does nothing but return a | |
def add(a:Int, b:Int) = a+b // implicit return type | |
assert(one == 1) | |
assert(self(1) == 1) | |
assert(add(1, 2) == 3) | |
// more Java-style definition. With this format you must specify return type otherwise it defaults to Unit | |
def subtract(a:Int, b:Int):Int = { | |
return a - b // "return" is optional here | |
} | |
assert(subtract(3, 2) == 1) | |
// this method doesn't need the = or parentheses | |
def printSomething { | |
println("something") | |
} | |
printSomething | |
// functions are objects no we can pass them around | |
def reduce(f: (Int, Int) => Int, a:Int, b:Int, c:Int) = f(f(a, b), c) | |
assert(reduce(add, 1, 2, 3) == 6) | |
assert(reduce(subtract, 3, 2, 1) == 0) | |
// Anonymous functions let you define them without a name (ie closures) | |
assert(reduce((a:Int, b:Int) => a*b, 4, 3, 2) == 24) | |
// Currying lets us bind some of the parameters and define a new function | |
// We'll do this to a normal function and function designed for currying | |
def multiply(a:Int, b:Int) = a*b | |
def multiplyCurry(a:Int)(b:Int) = a*b | |
assert(multiply(2,3) == 6) | |
// multiplyCurry(2,3) // throws an error about too many arguments | |
assert(multiplyCurry(2)(3) == 6) | |
// multiply(2)(3) // you can't do this: throws an error about missing arguments | |
var double = multiplyCurry(2) _ // the _ means this is a partially applied function | |
var triple = multiplyCurry(3) _ | |
assert(double(6) == 12) | |
assert(triple(6) == 18) | |
// we can convert normal functions to curryable | |
double = multiply(2, _:Int) | |
assert(double(6) == 12) | |
// another way to do the same thing by first making the function curryable | |
double = (multiply _ curried)(2) | |
assert(double(6) == 12) | |
/* | |
* Lists | |
*/ | |
// lists are typed and use generics (the type can be infered) | |
var l1:List[String] = List("foo", "bar", "baz") | |
var l2 = List("foo", "bar", "baz") | |
// operators ending in ':' operated and the right value. :: is a method on the list. | |
var l3 = "foo" :: "bar" :: "baz" :: Nil | |
assert(l1 == l2) | |
assert(l2 == l3) | |
assert(Nil == List()) | |
assert(l3 == Nil.::("baz").::("bar").::("foo")) | |
assert(l1 == "foo" :: List("bar", "baz")) | |
// basic list operations | |
assert(!l1.isEmpty) | |
assert(l1.nonEmpty) | |
assert(l1.length == 3) | |
assert(l1.head == "foo") | |
assert(l1.tail == List("bar", "baz")) // not the last element. everything except the head. | |
assert(l1.last == "baz") | |
assert(l1.apply(0) == "foo") | |
assert(l1(0) == "foo") | |
assert(l1.take(2) == List("foo", "bar")) | |
assert(l1.drop(2) == List("baz")) | |
assert(l1.dropRight(1) == List("foo", "bar")) | |
assert(List(1,2,3,4).dropWhile(_ < 3) == List(3,4)) | |
assert(List(1, 2, 3, 4).drop(1).take(2) == List(2, 3)) // getting a sub list | |
assert(l1.startsWith(List("foo"))) | |
assert(l1.endsWith(List("baz"))) | |
assert(List(1, 2) ::: List(3, 4) == List(1, 2, 3, 4)) // concatenating lists | |
assert(List(1, 2) ++ List(3, 4) == List(1,2,3,4)) | |
assert(List(1, 2) ++: List(3, 4) == List(1,2,3,4)) | |
assert(1 +: List(2,3) == List(1,2,3)) | |
assert(List(1,2) :+ 3 == List(1,2,3)) | |
assert(List(2, 1) == List(1, 2).reverse) | |
assert(List(2, 4, 6) == List(1,2,3).map(_*2)) | |
assert(List(2, 4, 6) == List(1,2,3).collect {case i:Int => i*2}) // TODO WHY? | |
assert(List(1,2,3).fold(0)((x,y) => x+y) == 6) | |
assert(List(1,2,3).reduce((x,y) => x+y) == 6) | |
assert(("" /: List("a","b","c"))((x,y) => x+y) == "abc") //reduces starting with "" left to right | |
assert((List("a","b","c") :\ "")((x,y) => x+y) == "abc") | |
assert( List(1,2,3).corresponds(List(2,4,6))((x,y) => y == x*2) ) | |
assert( List(1,2,3).diff(List(2,3,4)) == List(1) ) | |
assert(List(1,1,2,3).distinct == List(1,2,3)) | |
assert(List(1,2,3,4).find(_>2) == Some(3)) | |
assert(List(1,2,3,4).forall(_<5)) | |
assert(List(1,2,3,4).groupBy(_ % 2 == 0) == Map(false -> List(1, 3), true -> List(2, 4))) | |
assert( List(1,2,3,4).partition(_ % 2 == 0) == (List(2, 4),List(1, 3)) ) | |
assert(List(1,2,3,4).min == 1) | |
assert(List(1,2,3,4).max == 4) | |
var total = 0 | |
List(1,2,3).foreach(total += _) | |
assert(total == 6) | |
assert(List(3,4) == List(1,2,3,4).filter(_>2)) | |
// for comprehensions allow filtering and mapping in a concise syntax | |
l1 = for(x <- List(1,2,3,4) if x > 2) yield x.toString | |
assert(List("3", "4") == l1) | |
/* | |
* Maps | |
*/ | |
var m1 = scala.collection.mutable.Map("a" -> 1, "b" -> 2) | |
m1 += "c" -> 3 | |
assert(m1 == Map("a" -> 1, "b" -> 2, "c" -> 3)) | |
assert(m1("b") == 2) | |
assert(Map("a" -> 1) + ("b" -> 2) == Map("a" -> 1, "b" -> 2)) | |
assert(Map("a" -> 1, "b" -> 2) - "b" == Map("a" -> 1)) | |
// many of the same functions exist as with lists | |
/* | |
* Case classes and pattern matching | |
*/ | |
abstract class Expr | |
case class Number(n:Int) extends Expr | |
case class Multiply(e1:Expr, e2:Expr) extends Expr | |
case class Add(e1:Expr, e2:Expr) extends Expr | |
// These class are like creating an immutable class in Java with a parameterized constructor, hashCode, equals, toString, getters, and a static method for easier construction | |
var number = Number(1) | |
assert(number.n == 1) | |
assert(number == Number(1)) | |
assert(number != Number(2)) | |
assert(Add(Number(1), Number(1)) == Add(Number(1), Number(1))) | |
assert(List(number).contains(Number(1))) | |
assert(number.toString == "Number(1)") | |
// Pattern matching is like short-hand for lots of if's and instanceof's | |
def eval(e:Expr):Int = e match{ | |
case Number(n) => n | |
case Multiply(l,r) => eval(l) * eval(r) | |
case Add(l,r) => eval(l) + eval(r) | |
} | |
assert(eval( Multiply(Number(2), Add(Number(3), Number(1))) ) == 8) | |
def otherCases(v:Any):String = v match { | |
case Add(Number(n1), Number(n2)) => n1.toString + " + " + n2 // more complex pattern | |
case "foo" => "bar" // constant matching | |
case _ => "default" // the default | |
} | |
assert(otherCases("foo") == "bar") | |
assert(otherCases(3.14) == "default") | |
assert(otherCases(Add(Number(1), Number(2))) == "1 + 2") | |
assert(otherCases(Multiply(Number(1), Number(2))) == "default") | |
/* | |
* Traits | |
*/ | |
trait Commentable { | |
var comments = List[String]() | |
def comment(s:String) { comments = comments :+ s } | |
} | |
trait Likeable { | |
var likes = 0 | |
def like { likes += 1 } | |
} | |
class Article(title:String) | |
class BlogPost(title:String) extends Article(title) with Commentable with Likeable | |
// 2 different ways to use the traits | |
List( | |
new BlogPost("my post"), | |
new Article("special article") with Commentable with Likeable ).foreach | |
{ bp => | |
bp.comment("this is great!") | |
bp.comment("nice post") | |
bp.like | |
assert(bp.comments.length == 2) | |
assert(bp.likes == 1) | |
} | |
/* | |
* Implicit type conversion | |
*/ | |
// wrap objects to provide additional functionality | |
class IntWrapper(i:Int) { | |
def squared = i*i | |
} | |
implicit def wrapInt(i:Int) = new IntWrapper(i) | |
assert(4.squared == 16) | |
class Thing1(n:String) { | |
def name = n | |
} | |
class Thing2(n:String) { | |
def name = n | |
} | |
def thing2Name(thing:Thing2) { assert(thing.name == "bar") } | |
// wrather than wrapping we just do type coercion | |
implicit def thing2Converter(thing:Thing1) = new Thing2(thing.name) | |
implicit def thingConverter(s:String) = new Thing2(s) | |
thing2Name(new Thing1("bar")) | |
thing2Name("bar") | |
/* | |
* Ternary operator | |
*/ | |
// this does not work in Scala: if (true ? "foo" : "bar") | |
assert((if (true) "foo" else "bar") == "foo") | |
/* | |
* DSLs. A simple example putting together implicit conversion and operators | |
*/ | |
case class BooleanWrapper(b:Boolean) { | |
def and(that:Boolean) = b && that | |
def or(that:Boolean) = b || that | |
} | |
implicit def wrapBoolean(b:Boolean) = new BooleanWrapper(b) | |
assert(true and true or false == true) | |
/* | |
* Generics | |
*/ | |
case class MyTuple[A,B](a:A, b:B) { | |
// equivalent to: def aList:List[A] = List[A](a) | |
def aList = List(a) | |
} | |
var t1 = new MyTuple(1, "foo") // generic types are infered | |
assert(t1.a == 1) | |
assert(t1.b == "foo") | |
assert(t1.aList.apply(0) == 1) | |
// TODO covariant subtyping | |
/* | |
* Lazy variables | |
*/ | |
class Counter() { | |
lazy val a = System.currentTimeMillis | |
val b = System.currentTimeMillis | |
def doSomething { | |
lazy val c = System.currentTimeMillis | |
val d = System.currentTimeMillis | |
Thread.sleep(100) // c will be initialized after d when it's used | |
assert(d < c) | |
} | |
} | |
val counter = new Counter | |
counter.doSomething | |
assert(counter.b < counter.a) // a not initialized till now | |
/* | |
* XML literals | |
*/ | |
val author = "John Doe" | |
val title = "My Poem" | |
var poem = | |
<poem> | |
<title length="{title.size}" foo="bar">{title}</title> | |
<author>{author}</author> | |
<content><line>this is my poem</line></content> | |
</poem>; | |
assert(poem.isInstanceOf[scala.xml.Elem]) | |
assert((poem \ "content" \ "line").text == "this is my poem") | |
assert((poem \\ "line").text == "this is my poem") | |
assert((poem \ "title" \ "@length").text == "{title.size}") // no interpolation for attributes | |
assert((poem \\ "@foo" find { _.text == "bar" }).get.text == "bar") | |
// pattern matching lets you campare elements. note we get back a NodeSeq but need to match on a Node | |
var elementValue:String = null | |
(poem \ "author")(0) match { | |
case <author>{ text }</author> => elementValue = text.text | |
case <title>{ text }</title> => elementValue = "wrongvalue" | |
} | |
assert(elementValue == "John Doe") | |
/* | |
* Exception handling | |
*/ | |
// simple try/catch | |
try { | |
throw new IllegalStateException("demo") | |
} catch { | |
case e => println(e) // defined as Throwable | |
} | |
// try/catch using pattern matching | |
try { | |
throw new IllegalStateException("demo") | |
} catch { | |
// you can do multi-catch too! | |
case e:NullPointerException => println("null") | |
case e @ (_:IllegalStateException | _:IllegalArgumentException) => println("state or argument") | |
case _:java.util.zip.ZipException | _:java.io.EOFException => println("threw away my object") | |
case e => println("anything else") | |
} finally { | |
println("I'm always printed") | |
} | |
/* | |
* Tuples | |
*/ | |
var t = (1, "foo") // a Tuple2 | |
assert(t._1 == 1) | |
assert(t._2 == "foo") | |
val (x,y) = t // assiging multiple values from a tuple | |
assert(x == 1) | |
assert(y == "foo") | |
var t2 = 1->"foo" // used in defining Map key/values | |
assert(t == t2) | |
/* | |
* Regular expressions and case matching (normal Java stuff omitted) | |
*/ | |
val email = """(\w+)@(\w+).([a-zA-Z]{1,3})""".r | |
val name = """(\w+)\s+(\w+)""".r | |
def printMatch(s:String) { | |
s match { | |
case email(user, domain, tld) => println("user: "+user+" domain: "+domain+" tld: "+tld) | |
case name(first, last) => println("first: "+first+" last: "+last) | |
} | |
} | |
printMatch("[email protected]") | |
printMatch("Hubert Farnsworth") | |
var name(first, last) = "Bender Rodrigez" | |
assert(first == "Bender") | |
assert(last == "Rodrigez") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment