http://typesafe.com/platform/getstarted

https://github.com/froden/scalakurs/tree/master/oppgaver/del-1

SCALA

Introduksjonskurs

Faggruppe for Scala & JVM

Bekk

9. okt: Grunnleggende Scala

30. okt: Funksjonell programmering

11. nov: Webapp med Scala

SCALA

Et moderne, statisk typet, objekt-funksjonelt programmeringsspråk på JVM-en

Scala

Martin Odersky

Martin Odersky

Hvorfor Scala?

  • Elegant, konsis syntaks
  • Funksjonelt og objektorientert
  • Avansert typesystem
  • Høy ytelse

Community

scaladays.org twitter.com akka.io github.com playframework.com typesafe.com scala-lang.org liftweb.net 2013.flatmap.no stackoverflow.com meetup.com/scalabin

Hvem bruker Scala?

Bekk
Bruker scala

Basically, it’s the future

@al3x

Scalasyntaks 101

Definere ting


 // En ikke endrbar verdi, tenk final i Java
val fødselsnummer = "02017912345"

// En endrbar variabel
var navn: String = "Torbjørn Vatn"
                  

Method Man


// En metode med returverdi
def beregnAlder(personNummer: String): Int = {
  val alder = 0// kode som beregner alder
  alder
}

// En metode uten returverdi, tenk void i Java
def endreNavn(nyttNavn: String) {
  navn = nyttNavn
}
                  

Ifs and buts


// En if/else er en expression, så den returnerer en verdi
val er2StørreEnn4 = if (2 > 4) "JA" else "NEI"

// En enkel for som looper over en range fra 1 til 10
for (i <- 1 to 10) { println(i) }
                  

Let's get functional


// En funksjon fra to Ints til Boolean lagres i en verdi
val erStørreEnn = (x: Int, y: Int) => x > y

// Funksjonen kalles med to argumenter
if (erStørreEnn(2,4)) "JA" else "NEI"
                  

Oppgaver

oppgaver
Oppgaver: http://git.io/0aMB8Q
Slides: http://froden.github.io/scalakurs

klasser, objekter, traits

Objektorientert programmering

Klasser


class Person

val p = new Person
					

Medlemsvariabler og getters/setters


class Person(val name: String, var age: Int)

val p = new Person("Bob", 25)
p.name // Bob
p.age = 26
					    

Constructors

Primary constructor


class Person(age: Int) {
  val category = if (age < 35) "young" else "old"

  def sayHi = "Hi, I’m " + category
}

new Person(30).sayHi //Hi, I’m young
					

Auxillery constructors


class Date(val time: Long) {

  def this(date: Date) = this(date.time)
}
					    

Objekter


object Logger {
  def log(msg: String) = println(msg)
}

Logger.log("It’s all good!")
					

Companion-objekter


class Person(val name: String, val age: Int)

//I samme fil
object Person {
  def apply(name: String, age: Int) = new Person(name, age)
}

val p = Person("Frode", 33)
					    

Traits


trait Animal {
  def greet = "I’m an animal"
}

class Dog extends Animal
class Fox extends Animal
					
  • Abstrakte metoder og felter
  • Konkrete metoder og felter
  • Mixin composition

Traits


trait Animal {
  def say: String

  def greet = "I’m an animal that says " + say
}

class Dog extends Animal {
  def say = "woof"
}

class Fox extends Animal {
  def say = ???
}
					

Traits


trait Animal {
  def say: String
}
trait Fourlegged
trait Furry

class Dog extends Animal with Fourlegged with Furry {
  def say = "woof"
}
					

List

scala.immutable

  • Implementerer isEmpty, head og tail
  • O(n) på de fleste operasjoner
  • O(1) på prepend og head/tail-aksess
  • Egnet for rekursive løsninger basert på head/tail-aksess
  • Eksempler:
    scala> 4 :: List(1, 2, 3) // Prepend, O(1)
    res0: List[Int] = List(4, 1, 2, 3)
    scala> List(1, 2, 3) ::: List(4) // Append, O(n), foretrekk prepend
    res1: List[Int] = List(1, 2, 3, 4)

map()

  • map() tar inn en funksjon og utfører den på alle elementer
  • map() transformerer listen i henhold til funksjonen gitt inn
scala> val numbers = List(1,2,3)
numbers: List[Int] = List(1, 2, 3)

scala> numbers.map((x: Int) => x + 1)
res0: List[Int] = List(2, 3, 4)

filter()

  • filter() tar inn en funksjon (predikat)
  • filter() returnerer et subsett av listen hvor predikatet er sant
scala> val myList = List(1, 2, 3, 4, 5, 6)
myList: List[Int] = List(1, 2, 3, 4, 5, 6)

scala> myList.filter((x: Int) => x > 3)
res0: List[Int] = List(4, 5, 6)

List API eksempler

//Adder et element til begynnelsen av listen
def ::(x: A): List[A]

//Adder en liste til begynnelsen av listen
def :::(prefix: List[A]): List[A]

//Velger det første elementet i listen
def head: A

//Velger de resterende (foruten første) elementene i listen
def tail: A

//Velger de første n elementene i en liste
def take(n: Int): List[A]

//Velger de siste n elementene i en liste
def takeRight(n: Int): List[A]
                    

List API eksempler 2



//Velger alle elementer bortsett fra de første n elementene i en liste
def drop(n: Int): List[A]

//Lager en liste av par ut fra to lister
def zip[B](that: GenIterable[B]): List[(A, B)]

Pattern Matching

En avansert versjon av switch i Java

Java-liknende eksempel


def toYesOrNo(choice: Int) = choice match {
  case 1 => "yes"
  case 0 => "no"
  case _ => "error"
}
                        

scala> toYesOrNo(1)
res0: String = yes

scala> toYesOrNo(10)
res1: String = error
                        

Typesjekking


def f(x: Any) = x match {
  case i:Int => "integer: " + i
  case _:Double => "a double"
  case s:String => "I want to say " + s
}
                    

scala> f(2.0)
res0: String = a double
                    

Verktøy for funksjonell programmering

Med pattern matching:

def fact(n: Int) = n match {
  case 0 => 1
  case n => n * fact(n - 1)
}
                    
Uten pattern matching:

def fact(n: Int) =
  if (n == 0) 1
  else n * fact(n - 1)
                    

Case Classes

Klasser med god støtte for pattern matching

Eksporterer konstruktørparametere


case class Person(firstName: String, lastName: String)
                    

scala> val person = Person("Ola", "Nordmann")
person: Person = Person(Ola,Nordmann)

scala> person.firstName + " " + person.lastName
res0: String = Ola Nordmann
                    
  • Konstruktørparametere automatisk tilgjengelig etter instansiering
  • Reduserer boilerplate-kode

Pattern matching på Person


def finnOla(person: Person) = person match {
  case Person("Ola", 32) => println("Fant Ola 32!")
  case Person("Ola", alder) => println("Fant en annen Ola som er " + alder)
  case Person(navn, _) if navn.startsWith("O") => println("Fant en med navn på O")
  case Person(navn, alder) => println("Fant: " + navn + ", " + alder)
}
                

Pattern matching på lister


def checkIfFirstPersonIsOla(persons: List[Person]) =
  persons match {
    case List(Person("Ola", _), _*) => true
    case _ => false
  }
                

scala> val persons = List(Person("Ola", "test"),
    | Person("test", "test"))
persons: List[Person] = List(Person(Ola,test), Person(test,test))

scala> checkIfFirstPersonIsOla(persons)
res0: Boolean = true
                
_* betyr villlkårlig mange følgende elementer

SCALA

Teknikker for funksjonell programmering

Faggruppe for Scala & JVM

Bekk

Repetisjon

(og litt nytt)

Variabler og metoder


//immutable value, type inferred
val name = "Frode"

//immutable value with type
val name2: String = "Frode"

//method
def sayHiTo(name: String) = s"Hi $name"

//method with explicit return type
def add(a: Int, b:Int): Int = a + b
					

Alt er expressions


//result of last expression is returned
val date = {
  println("getting the date")
  new Date()
}

//if is an expression
def checkLength(str: String) = if (str.length < 50) "ok" else "too long"

//try catch is an expression
val head = try {
  Source.fromFile("app.log").getLines().take(10)
} catch {
  case _: IOException => List()
}
					

Funksjoner er verdier


//function value
val add = (a: Int, b: Int) => a + b

//convert method to function
val replaceAll = "Frode".replaceAll _
replaceAll("e", "3")
//res0: String = Frod3

//functions as arguments
def combine(a: Int, b: Int, func: (Int, Int) => Int) = func(a, b)

combine(2, 3, _ * _)
//res1: Int = 6
					

Klasser og objekter


class Person(val name: String, val age: Int) {
  override def toString = name + " " + age
}

//object with apply factory method
object Person {
  def apply(name: String, age: Int) = new Person(name, age)
}

Person("Frode", 33)
//res2: Person = Frode 33
					

case class og pattern matching


trait Tree

case class Node(left: Tree, right: Tree) extends Tree
case class Leaf(value: String) extends Tree

Node(Leaf("left"), Leaf("right")) match {
  case Node(_, Leaf(value)) => "right tree was leaf with value " + value
  case Node(_, right) => "right was " + right
}
//res3: String = right tree was leaf with value right
					

Generics

Som i Java, men med [T] i stedet for <T>


//value of generic type List with type argument String
val names: List[String] = List("Arild", "Sjur", "Frode")

//generic method with one type parameter
def lastElement[A](list: List[A]) = list.last

//generic mapping function with two type parameters
def map[A, B](value: A)(func: A => B): B = func(value)

map("Frode".toList)(lastElement)
//res4: Char = e
					

... men mye mer avansert!

SBT

Simple Build Tool

Byggeverktøy for Scala og Java

SBT

  • Enkel konfig for enkle prosjekter
  • Fleksibel konfig i Scalakode for avanserte prosjekter
  • Dependency-management ala Maven (Apache Ivy)
  • Kontinuerlig kompilering/testing/app-restart ved kodeendringer
  • ... og mye mer

Kom i gang med SBT

Start SBT interaktivt


$ cd scalakurs/oppgaver/del-2
$ ./sbt
[info] Loading project definition from /Users/frode/dev/scalaogjvm/scalakurs/oppgaver/del-2/project
[info] Set current project to scalakurs-ntnu-del2 (in build file:/Users/frode/dev/scalaogjvm/scalakurs/oppgaver/del-2/)
>
                    

Kjør testene kontinuerlig


> ~test
                    

Stop kjøring av tester


> <enter>
                    

Andre nyttige kommandoer


> clean
> compile
> run
                    

Eclipse og IntelliJ

Generer prosjektfiler for Eclipse


> eclipse
                    

Generer prosjektfiler for IntelliJ


> gen-idea
                    

Deretter kan du velge File -> Open -> scalakurs/oppgaver/del-2

Oppgaver

oppgaver
Oppgaver: http://git.io/zLhs5Q
Slides: http://froden.github.io/scalakurs

For comprehensions

For comprehensions


def even(start: Int, end: Int) =
    for (i <- start until end if i % 2 == 0) yield i
                    

scala> even(0, 20)
res0: Vector[Int] = Vector(0, 2, 4, 6, 8, 10, 12, 14, 16, 18)
                    

Flere generatorer


scala> for(  x <- (1 to 2 );  y <- (1 to 3)  ) yield (x,y)

res0: Vector[(Int, Int)] = Vector((1,1), (1,2), (1,3), (2,1), (2,2), (2,3))
                                        

Guards


def getFactors(n: Long) =
  for (x <- ( 1 to n ); if n % x == 0) yield x
                    

scala> getFactors(20)
res0: Vector[Int] = Vector(1, 2, 4, 5, 10, 20)
                    

Syntaktisk sukker


for(x <- c1; y <- c2; z <-c3) {...}
// compiles to
c1.foreach(x => c2.foreach(y => c3.foreach(z => {...})))
                    

for(x <- c1; y <- c2; z <- c3) yield {...}
// compiles to
c1.flatMap(x => c2.flatMap(y => c3.map(z => {...})))
                    

for(x <- c; if cond) yield {...}
// compiles to (in Scala 2.8+)
c.withFilter(x => cond).map(x => {...})
                    

Option

For å representere "noe" eller "ingenting"

Option[A]

Some[A]   None

Eksempel


val something: Option[String] = Some("A string")
val nothing: Option[String] = None

something.isEmpty
//res4: Boolean = false
nothing.isEmpty
//res5: Boolean = true

something.getOrElse("")
//res6: String = A string
nothing.getOrElse("")
//res7: String = ""
                    

«Null References: The Billion Dollar Mistake»

Sir Tony Hoare

Problemet med null


val name: String = request.getParameter("name")

val upperCaseName = if (name != null) {
  val trimmed = name.trim
  if (!trimmed.isEmpty) {
    trimmed.toUpperCase
  } else {
    null
  }
} else {
  null
}
                    

Option løser problemet


val name: Option[String] = request.getParameter("name")

val upprCaseName = name map (_.trim) filter (!_.isEmpty) map (_.toUpperCase)
                    

Option er en collection med 0 eller 1 element


val myOptional: Option[String] = Some("Hello")

myOptional.foreach(println)
// Hello
myOptional.exists(_.startsWith("H"))
//res0: Boolean = true
myOptional.map(_.toLowerCase)
//res2: Option[String] = Some(hello)
myOptional.flatMap(s => Some(s + " Frode"))
//res3: Option[String] = Some(Hello Frode)
myOptional.getOrElse("!!")
//res4: String = Hello
                    

Kan pattern matches


val optionalPerson: Option[Person] = Some(Person("Frode"))

optionalPerson match {
  case Some(Person("Frode")) => println("Fant Frode")
  case Some(_) => println("Fant noen andre")
  case None => println("Ikke en person")
}
                    

Kan brukes i for-comprehensions


val firstName: Option[String] = Some("Arild")
val lastName: Option[String] = Some("Nilsen")

val fullName = for {
  first <- firstName
  last <- lastName
} yield first + " " last
                    

Try

Funksjonell feilhåndtering

Try[A]

Succes[A]   Failure[A]

Kaste og fange exceptions


case class Kunde(alder: Int)
class Sigaretter
case class ForUngException(melding: String) extends Exception(melding)

def kjøpSigg(kunde: Kunde): Sigaretter =
  if (kunde.alder < 18)
    throw ForUngException(s"Kunden må være over 18, men er ${kunde.alder}")
  else new Sigaretter
                  

val ungKunde = Kunde(15)
val melding = try {
  val røyken = kjøpSigg(ungKunde)
  s"Her har du $røyken din!"
} catch { 
  case ForUngException(mld) => mld
}
println(melding)
                    

Prøve og feile


case class Kunde(alder: Int)
class Sigaretter
case class ForUngException(melding: String) extends Exception(melding)

def kjøpSigg(kunde: Kunde): Sigaretter =
  if (kunde.alder < 18)
    throw ForUngException(s"Kunden må være over 18, men er ${kunde.alder}")
  else new Sigaretter
                  

val ungKunde = Kunde(15)
val melding = Try(kjøpSigg(ungKunde)) match {
  case Success(røyken) => s"Her har du $røyken din!"
  case Failure(e) => e.getMessage
}
println(melding)
                    

Komme seg ut av knipa


case class Kunde(alder: Int)
trait Produkt
class Sigaretter extends Produkt
class Tyggis extends Produkt
case class ForUngException(melding: String) extends Exception(melding)

def kjøpSigg(kunde: Kunde): Try[Produkt] =
  if (kunde.alder < 18)
    Failure(ForUngException(s"Kunden må være over 18, men er ${kunde.alder}"))
  else
    Success(new Sigaretter)
                  

val ungKunde = Kunde(15)
val produkt = kjøpSigg(ungKunde) recover {
  case fue: ForUngException => new Tyggis
}
println(s"Hurra jeg fikk kjøpt ${produkt.get}")
                  

Success biased


val keyValues = for {
  keys <- Try(Source.fromFile("keys.txt").getLines())
  values <- Try(Source.fromFile("values.txt").getLines())
} yield keys zip values toMap

val result: Map[String, String] = keyValues.getOrElse(Map())
                  

Jobbe med Try

  • getOrElse
  • map / flatMap
  • filter / foreach
  • for comprehensions
  • pattern matching

Futures og Promises

  • SIP-14 - redesign av scala.concurrent
  • Introdusert i Scala 2.10
  • Placeholder objekt for et resultat som ennå ikke eksisterer

SIP-14 definisjon

A Future is a read-handle to a single value (read-many) that may be available within a specific time-frame
A Promise is a write-handle to a single value (write-once) that should be made available within a specific time-frame

Hello Future

println("Test print before future")
val s = "hello"

val f = future {
  Thread.sleep(10)
  s + " future!"
}

println("Test print after future")

// Completely asynchronous
f.onSuccess { case s => println(s) }

// Blocks until the future is ready
Await.ready(f, Duration.Inf)
Output:
Test print before future
Test print after future
hello future!

Grunnleggende operasjoner på Futures

//Asynchronously processes the value in
//the future once the value becomes available.
def foreach[U](f: T => U): Unit

//Creates a new future by applying a function to the successful
//result of this future.
def map[S](f: T => S): Future[S]

//Creates a new future by applying a function to the successful
//result of this future, and returns the result of the function
//as the new future.
def flatMap[S](f: T => Future[S]): Future[S]

//Creates a new future by filtering the value of the current future
//with a predicate.
def filter(p: T => Boolean): Future[T]

Feilhåndtering

val riskyRes = future { riskyWork() }
val safeRes = future { safeWork() }

val finalRes = riskyRes recoverWith {
  case e: IllegalArgumentException => safeRes
}

Futures er composeable

val keys = future { readFile("keys.txt") }
val values = future { readFile("values.txt") }

val data = keys.zip(values)

val hashMap = data.map((ls: (List[String], List[String])) => {
  ls._1.zip(ls._2).toMap
})

hashMap.recover {
  case e: FileNotFoundException => {
    Map[String, String]()
  }
}.onSuccess {
  case map => {
    println(map)
  }
}

Await.result(hashMap, Duration.Inf)

Gjør oppgaver

  • oppg5_futures (Få testene grønne)
  • Kikk på Work-klassen (og perform() metoden)

"Futures" vs. "Promises"

  • "Future", "promise", "delay" og "deferred" brukes om hverandre
  • Komplementære primitiver
  • JavaScript-verden bruker kun "Promise"

Producer Consumer Eksempel

val p = promise[T]
val f = p.future

val producer = future {
  val result = produceSomething()
  p success result
  continueDoingSomethingUnrelated()
}

val consumer = future {
  startDoingSomething()
  f onSuccess {
    case res => doSomething(res)
  }
}

Ressurser

Takk for innsatsen, folkens!

9. okt: Grunnleggende Scala

30. okt: Funksjonell programmering

11. nov: Webapp med Scala

Intro til Scalatra

Scalatra

  • Web micro-framework
  • Router HTTP-requests til koden din
  • http://www.scalatra.org/guides/

Routes

  • En scalatra-route er en HTTP-methode
  • (dvs GET,PUT,POST eller DELETE)
  • koblet sammen med en URL-matcher

Routes eksempel

class Articles extends ScalatraServlet {

  get("/articles/:id") {  //  <= dette er en route-matcher
    // dette er en action
    // som henter en artikkel med den spesifiserte :id
  }
  post("/articles") {
    // submit/lage en artikkel
  }
  put("/articles/:id") {
    // oppdatere en artikkel med en spesifikk :id
  }
  delete("/articles/:id") {
    // slette en artikkel med en spesifikk :id
  }

}

Action eksempel

class Articles extends ScalatraServlet {

  get("/articles/:id") {
    <html>
      <body>
        <p>Ah, article with id {params("id")} is a classic!</p>
      </body>
    </html>
  }

}
        

MongoDB

mongoDB

  • open source, NoSQL database
  • lagrer JSON-aktige dokumenter
  • kan indeksere på ønsket atributt

MongoDB dokument

{
    name: "sue",
    age: 26,
    status: "A",
    groups: [ "news", "sports"]
}
        

MongoDB oppsett

  • http://www.mongodb.org/downloads
  • tar zxvf mongodb-linux-x86_64-2.4.8.tgz
  • mkdir -p /home/sjur/mongodbdata
  • mongodb --dbpath /home/sjur/mongodbdata
  • ... [initandlisten] waiting for connections on port 27017

MongoDB add data

mongo
help
db.x.insert({_id: 10, type: "misc", item: "card", qty: 15})
db.x.update({type:"book", item:"journal"},{$set:{qty:9}}, {upsert:true})
db.x.save({type: "book", item: "notebook", qty: 40})

MongoDB query

db.inventory.find({})
db.x.find({type: "snacks"})
db.x.find({type: {$in: ['food', 'snacks']}})
db.x.find({type: 'food', price: {$lt: 9.95}})
db.x.find({$or: [{ qty: {$gt: 100}}, {price: {$lt: 9.95}}]})

AngularJS

angularJS

JavaScript rammeverk for utvikling
av single-page applikasjoner

5 Awesome AngularJS Features

  1. Two Way Data-Binding
  2. Templates
  3. MVC
  4. Dependency Injection
  5. Directives

Example view




    

Example Controller


function MainController($scope, dataService) {
    $scope.showError = false;

    dataService.getAllArticles().then(
        function(articles) { $scope.articles = articles; },
        function() { showError('feil ved henting av artikler'); }
    );

    function showError(message) {
        $scope.showError = true;
        $scope.errorMessage = message;
    }

    $scope.submitArticle = function(article) {
        dataService.addArticle(article).then(
            function() { $scope.articles.push(article); },
            function() { showError('feil ved lagring av artikkel'); }
        );
    }
};
    

Example Model


app.factory('dataService', function($http) {
    return {
        getAllArticles: function() {
            return $http.get('/articles').then(function(resp) {
                return resp.data;
            });
        },
        addArticle: function(article) {
            return $http.post('/articles', article).then(function(resp) {
                return resp.data;
            });
        }
    }
});
    

Blog API


#ArticlesController
GET    /articles               #get all articles
POST   /articles               #create new article
GET    /articles/:id           #get single article
PUT    /articles/:id           #update existing article
DELETE /articles/:id           #remoeve article
POST   /articles/:id/comments  #create new comment on article
DELETE /articles/:id/comments  #delete all comments from article
            

Blog API


#article with comments
{
    "_id": "1234zxcvdas",
    "author": "Frode",
    "title": "A blogpost",
    "content": "Loong and informative post"
    "comments": [
        {
            "author": "Sjur",
            "content": "Nice post!",
        },
        {
            "author": "Arild",
            "content": "Could be shorter."
        }
    ]
}
            

Nyttige lenker

Takk for innsatsen alle sammen!