home
firma

Advanced live coding scala tutorial

download as text file

// =================================================================================
// = Advanced live coding scala tutorial given on HaL6 (6th Haskell in Leipzig Workshop)
// =
// = 7th Oct 2011 in Leipzig
// = Alf Richter, iba Consulting Gesellschaft mbH&Co.KG, Leipzig
// = Duration: more than 90 min

// =================================================================================
// -- CONTENTS

//= FUNCTIONS & COLLECTIONS

//-- val / var  = immutable / mutable  reference
//-- function syntax/convention
//-- anonymous function
//-- collections (default: immutable collections)
//-- for comprehension


//= CLASSES & OBJECTS

//-- class definition and instances of a class
//-- companion object
//-- getter/setter
//-- override getter/setter
//-- equality of objects


//= CASE CLASSES, MATCHING and TRAITS

//-- case class and structural matching
//-- match by structure
//-- extractors
//-- sealed case class
//-- traits


//= ADVANCED TOPICS and STANDARD API EXAMPLES

//-- implicit parameters
//-- regexp
//-- xml
//-- process api (>= 2.9)
//-- implement a cd command using implicit tricks


//= TYPES, PACKAGES and VISIBILITY

//-- type hierarchy
//-- packages definition and imports
//-- Predef & default imports
//-- visibility


//= COMPILING and BUILDTOOL

//-- Hello World and compiling
//-- SBT - Simple Build Tool (version >= 0.10)

// =================================================================================
// -- Let's start

//- to run this you need scala in version >= 2.9

//- with $ prefix: input in shell
//- without prefix: input in scala interpreter 
//- CTRL-D : input a keyboard shortcut

//- start the scala interpeter 
$ scala

//- copy and paste the following examples into the scala interpreter

// =================================================================================
// = FUNCTIONS & COLLECTIONS

// ---------------------------------------------------------------------------------
// -- val / var  = immutable/mutable reference

val x = 10 // val stands for value (immutable reference)
x = 11 // error

var y = 2 // var stands for variable (mutable reference)
y = 12 // ok

// ---------------------------------------------------------------------------------
// -- function syntax/convention

//- with side effects def f(x:Int){ ... }
def printInt(y:Int) { println(y) }

//- without side effects def f(x:Int) : Int = { ... }
def plusTwo(y:Int) = y + 2

// -- anonymous function
//- standard form is
//- (x : Int) => x + 1

//- functions are values / objects
val f = (x : Int) => x + 1
val g = () => 42 // function without parameters


//-- form with positional parameter
//- define two helper functions
def f1(f : Int => Int) = f
def f2(f : (Int,Int) => Int) = f

f1(_ + 1)  // (x : Int) => x + 1
f1(_ + 1)(2) == 3

f2(_ + _)  // (x : Int, y : Int) => x + y
f2(_ + _)(2,3) == 5

//- functions with empty parameter list can be called without brackets

def printHello() : Unit = println("Hello")
printHello()
printHello

//- class/object member functions can be applied without brackets or point
//- suffix notation
1.toDouble // the method call to the instance 1 of Int class can be written as
1 toDouble
 
//- infix notation
1.+(2) == 1 + 2 // calling method + on instance 1 of Int class 
(1 :: 2 :: Nil) map ( x => x+2 )  == 3 :: 4 :: Nil // class List has a method ::

//- Note: method ending with colon : , e.g. :: , ::: , +: are right-associative
Nil.::(4).::(3) == 3 :: 4 :: Nil

// ---------------------------------------------------------------------------------
// -- Collections (default: immutable collections)

//- From a list produce a list of Duplicate Entries
List("haskell", "scala", "haskell", "cal").
        groupBy(x=>x).
        filter(_._2.size > 1).
        map(_._1) == List("haskell")

// - Two ways to construct a map, 
// - Map() is definied as immutable map in Predef package (see below)
Map("eins" -> 1, "zwei" -> 2)
val m = Map() + ("eins" -> 1) + ("zwei" -> 2)

m get "zwei" == 2 // == m.get("zwei")
m get "drei" // error
m.getOrElse("drei",0) == 0

//- convert and format the map to string
m.mkString("["," , ","]")
m.mkString("\n")

// -- for comprehension
//- range generator
1.to(4) //== Range(1,2,3,4)
0 to 4 by 2 //== Range(0, 2, 4)

//- calling actions like foreach return type  : Unit
for(i <- 1 to 10) println(i)

//- produce a result sequence (combine map, filter ...) : Seq[T]
for( i <- 1 to 10 ; j <- i to 10 ; if i % 2 == 0 ) yield (i,j,i*j)
for ( (x,y) <- (10 to 4 by -2).zipWithIndex) yield (x,y)

// =================================================================================
// = CLASSES & OBJECTS

// ---------------------------------------------------------------------------------
// -- class definition and instances of a class

//- simple class
class Foo
new Foo() // or 
new Foo

//- class with one member and one default constructor
class Foo { var x = 1 }
new Foo().x == 1 // or 
new Foo.x == 1

//- normal class with constructor
class Foo( x : Int)  // no member x!

//- members are public by default
class Foo( x1 : Int) { val x = x1 }

//- short hand for the previous
class Foo( val x : Int) // member Foo.x
new Foo(1).x == 1

class Point(val x : Int, val y : Int){
   println("constructing a point") // called in primary constructor!

   def this() = this(0,0) // must call primary constructor

   def this(xy : Int) = this(xy,xy)
   override def toString = "Point(" + x + ", " + y + ")"
}

// ---------------------------------------------------------------------------------
// -- companion object
//- contains member function/values which are the same for all instances (java: static)
// class and companion object have to be on one line/input sequence in scala interpreter
// or definied in one scala file
class Foo( val x : Int) ; object Foo { def apply(x : Int) = new Foo(x)  }

//- Factory methods
Foo(1).x == 1 // no 'new' needed, short cut for Foo.apply(1)


// ---------------------------------------------------------------------------------
// -- getter/setter
// only getter
class Foo( val x : Int)
val f = new Foo(1)
f.x // prints 1
f.x = 2 // error

// getter and setter
class Foo( var x : Int)
val f = new Foo(1)
f.x == 1
f.x = 2 // assignment to member Foo.x
f.x == 2

// ---------------------------------------------------------------------------------
// -- override getter/setter
class Foo { val x : Int = 0 }

//- getter
class Foo { 
  private[this] val _x : Int = 0; // private field, [this] means instance private
  def x = { println("getter called") ; _x }
}

(new Foo).x

//- setter, x_= is a function name (!!), you must write a getter function here
class Foo {
    private[this] var _x : Int = 0;
    def x = _x ;
    def x_= (x : Int) : Unit = { println("setting x= " + x) ; _x = x }
}

(new Foo).x = 100

// -- equality of objects
// - == / != checks for value equality and not for reference equality
// - implement equals AND hashcode for own types based on class members
// - reference equality check with AnyRef.eq or AnyRef.ne

// =================================================================================
// = CASE CLASSES, MATCHING and TRAITS

// ---------------------------------------------------------------------------------
// -- case class and structural matching

// - case class vs class
//- Pattern matching,
//- Construction of instances without using the new keyword,
//- Constructor arguments are accessible from outside (as val)
//- toString method prints name and values of arguments,
//- equals method compare two instances of the same case class structurally rather than by identity.
//- hashCode method using the hashCodes of constructor arguments.

//-- desugared example, to show generated methods
$ cat > Foo.scala
case class Foo(x:Int)

CTRL-D

//- remove syntactic sugar with scalac -print
$ scalac -print Foo.scala  

//- compile and display class and object definition as seen from scala
$ scalac Foo.scala # generate .class files
$ scalap Foo # display class
$ scalap Foo$ # display object


//- display bytecode as seen from java
$ javap Foo # display class
$ javap Foo$ # display object

//-- more case class examples
case class Point( x : Int, y : Int)

//- updating case class instances using copy and named parameters
val p = Point(1,2)
val q = p.copy( y = 100 )
p.x == q.x
p.y == 2
q.y == 100

// -- match by structure
case class Line( p : Point , q : Point)

val p1 = Point(10,10)
val p2 = Point(10,100)

val l = Line(p1,p2)

//- extract point coordinates of the line
val s = l match {
    case Line( Point(x1,y1), Point(x2,y2) ) => x1 + "," + y1 + " - " + x2 + ", " + y2
}

//- using @ to capture a object and using _ as joker
val s = l match {
    case Line( _ , q@Point(x2,y2) ) => q
}
// --------------------------------------------------------------------------------
// -- extractors

//-- unapply can return the following
//- Boolean -- only match no extraction 
//- Option[T] -- match and extract
//- Option[(T1,..,Tn)] -- match and extract as tupel
//- Option[Seq[T]] -- match and extract as list

// Extractor which converts the string to an Int if possible.
object AsInt{def unapply(s:String): Option[Int] =try{ Some(s.toInt) }catch{ case _ => None }}

"77" match { case AsInt(n) => println(n) ; case _ => println("nothing") }

//- Extractor Sequence
object Slashes { def unapply(s : String) : Option[List[String]] = Some(s.split("/").toList) }
"a/b/c" match { case Slashes(x::y::_) => List(x,y)  }

// ---------------------------------------------------------------------------------
// -- sealed case class
// - all case classes in one file extending one common sealed abstract class

sealed abstract class Thing
case class Foo() extends Thing
case class Bar() extends Thing

// - compiler warning if some cases are missing
def f( x : Thing) : String = x match { case Foo() => "foo" }
// <console>:10: warning: match is not exhaustive!
// missing combination  Bar

// ---------------------------------------------------------------------------------
// -- traits

// - like java interfaces or haskell type classes
// - can have members and implementation
// - one type can be extends multiple traits

trait Color {
    private var _color : Int = 1
    def color = _color
    def color_( c : Int ) = { this._color = c }
}

trait Center {
    private var _center: (Int,Int) = (0,0)
    def center = _center
    def center_= (c : (Int,Int)) = { _center = c }
    
    // tuple extraction: (10,20)._1 == 10 and (10,20)._2 == 20 and 
    def moveBy( p : (Int, Int)) = { _center = (_center._1 + p._1, _center._2 + p._2) ; this }
}

class Shape extends Center with Color {
    override def toString = "color: " + color + " center: " + center
}

val s = new Shape

s.center
s.color
s.color = 0
s moveby (10,10)
s.center
s.color

//- Traits can be mixed in dynamicly
class Thing
new Thing with Color with Center

//- Traits can be used as interceptors
trait LoggableCenter extends Center {
    override def moveBy( p: (Int,Int) ) = { println("moved by " + p) ; super.moveBy(p) }
}

class Shape extends LoggableCenter with Color {
    override def toString = "color: " + color + " center: " + center
}

//- Traits can have contracts
trait ColorPrinter {
    def color : Int // contract method

    def print = { println("color = " + color)}
}

val s2 = new Shape with ColorPrinter
s2.print

new Thing with Center with ColorPrinter // error: object creation impossible, no method color ...

// =================================================================================
// = ADVANCED TOPICS and STANDARD API EXAMPLES

// ---------------------------------------------------------------------------------
// -- implicit parameters

implicit val e = "UTF8"
def openFile(filename : String)(implicit encoding : String) = println("encoding=" + encoding)
openFile("tmp.txt")
//- implicit parameter are resolved by type and not by name !!

// ---------------------------------------------------------------------------------
// -- regexp
// - Quoted multiline-string with """ """ 
"""\d\.\d""".r findFirstIn "Version 9.1" // == Some("9.1")

// ---------------------------------------------------------------------------------
// -- xml
val x = <dir name="home"><file name="a.txt"/><file name="b.txt" /> </dir>

x \\ "dir" \ "@name" text
x \\ "file" \\ "@name" map (_.text)

//-- construct xml using process api (see below), examples only works in unix like enviroment 
import sys.process._

//- find files in current dir and convert to string list
val s = ("find ." #| "grep -v .hg" lines_! ) toList

//- construct a dir xml node with file nodes using file list s  
val x = <dir name="hal6">{s map {f => <file name={f}/> } } </dir>

//- format the nodes
new scala.xml.PrettyPrinter(80,2).formatNodes(x)

// ---------------------------------------------------------------------------------
// -- process api (>= 2.9)
import sys.process._
import java.io.File

"find ." #| "grep -v .svn" #> new File("files.txt") !
"echo test" #> new File("test2.txt") !
"ls " !

"cat " #< new File("test.txt") #> new File("test-dir/test2.txt") !
val logger = ProcessLogger((o: String) => println("out " + o),(e: String) => println("err " + e))

"ls . " ! logger
"ls with err " ! logger
"find ." !
"find ." #| "grep test2" ! logger

//- ! - runs process (ProcessLogger gets output as tupple std  err)
//- #< redirect from stdin(File)
//- #> redirect to stdout(File)
//- #| shell pipe
//- !! output as one (multline) String 
//- lines_! as Stream, convert to String list with toList

// ---------------------------------------------------------------------------------
// -- implement a cd command using implicit tricks
//- see http://www.scala-lang.org/node/10985

// =================================================================================
// = TYPES, PACKAGES and VISIBILITY
     
// ---------------------------------------------------------------------------------
// -- type hierarchy

//- http://joelabrahamsson.com/entry/learning-scala-type-hierarchy-and-object-equality
//- purely object oriented typ system
//- two kinds of types: value und reference
//- Any is parent of all classes
//- Nothing is child of all classes
//- AnyRef is used as parent for Java objects

// ---------------------------------------------------------------------------------
// -- packages definition and imports

//- copy and paste (from BEGIN to END) into scala file PackageExample.scala 

// BEGIN of PackageExample.scala
package de {
  package ibacg {
      class Bar
  }
}

//// same as above
//package de;
//package ibacg;
//class Bar

//- extending above package with new class and object
package de.ibacg {
  class Bar2
  object Constants { val pi = 22 / 7; }
}

//- _root_ to access package root
package b { class B }

package a.b { class A {  val x = new _root_.b.B() } }

//- imports

object PackageExample {
  // local import
  {
    import de.ibacg.Bar;
    new Bar
    import de.ibacg.Bar2;
    new Bar2
  }
  {
    import de.ibacg.{Bar,Bar2}
    new Bar ; new Bar2 ; Constants.pi
  }
  {
    import de.ibacg._
    new Bar ; new Bar2  ; Constants.pi
  }
  {
    import de._;
    import ibacg.Bar;
    new Bar
  }
  {
    // local import of members
    import de.ibacg.Constants._
    println(pi)
  }
  // error here: println(pi)

  {
    // renaming of imported things
    import scala.collection.immutable.{Map => ImmutableMap}
    import scala.collection.mutable.{Map => MutableMap}
    ImmutableMap(1 -> 2)
    MutableMap(3 -> 4)
  }
}
// END of PackageExample.scala

// ---------------------------------------------------------------------------------
// -- Predef & default imports

//- the packages scala, Predef, java.lang are automatically imported
//- e.g. includes conversions (boxing/unboxing)
//- and defaults for immutable collections like Map(), List(), Set()

// ---------------------------------------------------------------------------------
// -- visibility
//- public is default

//- private member only in current scope and companion visible
//- private[this] and private[de.ibacg.service.impl]

//- protected only visible in subclasses and companion/companions of subclasses
//- protected[this] and protected[de.ibacg.service.impl]


// =================================================================================
// = COMPILING and BUILDTOOL

// ---------------------------------------------------------------------------------
// -- Hello World and compiling

//- App trait is used since scala 2.8
//- finish input to cat with CTRL-D
$ cat >> HelloWorld.scala
object HelloWorld extends App {
    println("Hello World!")
}

CTRL-D

//- calling the compiler
$ scalac HelloWorld.scala

//- starting the compiled program
$ scala HelloWorld
Hello World!

//-  starting in interpreter
$ scala -cp .
HelloWorld.main(new Array(0))

// ---------------------------------------------------------------------------------
// -- SBT - Simple Build Tool (version >= 0.10)
//- installation instruction https://github.com/harrah/xsbt/wiki/Setup

//- convert to sbt 

// move files to maven style layout and clean up class-files
$ mkdir -p src/main/scala
$ mv *.scala  src/main/scala
$ rm *.class

//- create build file
$ cat >> build.sbt
name := HelloWorld   // after an property insert empty line or ; 

version := 0.1

// scalaVersion := "2.9.1"

// libraryDependencies += "junit" % "junit" % "4.8" % "test"

CTRL-D

//- sbt tasks (ctrl-c or ctrl-d to cancel)
$ sbt compile # compile project assuming standard maven layout
$ sbt ~compile # compile and watch src dirs for changes
$ sbt ~run # run the main; recompile and run after changes in source files
$ sbt console # start interpreter with current project on class path

newsticker