Skip to content

Instantly share code, notes, and snippets.

@mrdanadams
Created August 3, 2011 16:36
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mrdanadams/1123089 to your computer and use it in GitHub Desktop.
Save mrdanadams/1123089 to your computer and use it in GitHub Desktop.
Scala Language Examples
/*
* 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("foo@example.com")
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