SlideShare a Scribd company logo
SCALAWORKSHOPKRAKOW
OCTOBER2017-DAY1
Fredrik Vraalsen - Data Platform
1
TOPICS
• Language features
• Functional programming
• Best practices
2
AGENDADAY1-THEBASICS
• Intro
• Scala basics
• Expressions, methods and functions
• Collections I
3
AGENDADAY2-FP
• Classes and Pattern Matching
• Collections II
• Laziness, implicits, DSLs
• QA & Wrapup
4
SCALARESOURCES
• API docs: https://www.scala-lang.org/api/current/
• Guides and overviews: http://docs.scala-lang.org/overviews/
• Tutorials: http://docs.scala-lang.org/tutorials/
5
TWITTER-EFFECTIVESCALA
• Best practices
• (Formatting)
• Usage of language features
• http://twitter.github.io/effectivescala/
6
BEFOREWESTART
7
git clone https://github.schibsted.io/fredrik-vraalsen/scala-workshop.git
SCALA
• Hybrid FP + OO language
• Developed at EPFL by Martin Odersky
• First public release in 2004, 2.0 in 2006
8
SCALA
9
https://dzone.com/articles/scala-liftoff
SCALA
• Current release 2.12 in 2016
• Focus on Java 8 interop
• Scala 3.0 in the works (codename Dotty)
10
SCALABASICS
HELLOWORLD
~ $ cat > helloworld.scala
println("Hello world!")
^D
~ $
12
HELLOWORLD
~ $ cat > helloworld.scala
println("Hello world!")
^D
~ $ scala helloworld.scala
Hello world!
~ $

13
LITERALS
"Fredrik" // String
'c' // Char
true / false // Boolean
42 // Int
2147483648L // Long
3.1415927f // Float
3.141592653589793 // Double
Byte (8 bit)
Short (16 bit)
14
VALUES
val name: String = "Fredrik"

val age: Int = 41
15
TYPEINFERENCE
val name = "Fredrik" // String
val age = 41 // Int
16
VARIABLES
val name = "Fredrik" // String
val age = 41 // Int
age = 42 // Compile error!
17
VARIABLES
val name = "Fredrik" // String
var age = 41 // Int
age = 42 // Yay!!?
18
STRINGS
val greeting = "Hello " + name + "!"
val greeting = s"Hello $name!"
val greeting = "Have some more %.5f!".format(Math.PI)
val greeting = s"""Hello "$name!"
This is a
multiline greeting!"""
19
STRINGS
val greeting = "Hello " + name + "!"
val greeting = s"Hello $name!"
val greeting = "Have some more %.5f!".format(Math.PI)
val greeting = s"""|Hello "$name!"
|This is a
|multiline greeting!""".stripMargin
20
CASECLASSES
case class Person(name: String, age: Int)
val p = Person("Fredrik", 41)
p.name // "Fredrik"
p.age // 41
21
EQUALITY
val p1 = Person("Fredrik", 41)
val p2 = Person("Fredrik", 41)
p1 == p2 // true
p1 eq p2 // false
p1.name == p2.name // true
p1.name eq p2.name // true
p1.age == p2.age // true
p1.age eq p2.age // error: value eq is not a member of Int
22
REPL
~ $ scala
Welcome to Scala 2.12.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_152).
Type in expressions for evaluation. Or try :help.
scala>
23
REPL
~ $ scala
Welcome to Scala 2.12.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_152).
Type in expressions for evaluation. Or try :help.
scala> case class Person(name: String, age: Int)
defined class Person
scala>
24
REPL
~ $ scala
Welcome to Scala 2.12.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_152).
Type in expressions for evaluation. Or try :help.
scala> case class Person(name: String, age: Int)
defined class Person
scala> val p = Person("Fredrik", 41)
p: Person = Person(Fredrik,41)
scala>
25
REPL
~ $ scala
Welcome to Scala 2.12.4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_152).
Type in expressions for evaluation. Or try :help.
scala> case class Person(name: String, age: Int)
defined class Person
scala> val p = Person("Fredrik", 41)
p: Person = Person(Fredrik,41)
scala> p.name
res0: String = Fredrik
scala>
26
SCALATEST
• Multiple testing styles (xUnit, BDD, etc.)
• Asserts or matchers
27
SCALATEST
28
SCALATEST
29
SCALATEST
result should be (3)
result shouldBe 3
result should equal (3)
result shouldEqual(3)
result should === (3)
result should not be 3
result should !== (3)
result should be < 7
result should have length 3 // or size
result should startWith (“Hello”) // or endWith / contain
30
SCALATEST
• Run tests
• With Gradle: ./gradlew test -t
• In IDE: Ctrl+Shift+F10 or Ctrl+Shift+R
• More info at http://scalatest.org/
31
EXERCISE1
32
EXPRESSIONS,METHODSAND
FUNCTIONS
EXPRESSIONS
• Every expression returns a value
34
EXPRESSIONS
var sign: String = _ // null
if (i >= 0) {
sign = "Positive"
} else {
sign = "Negative"
}
35
EXPRESSIONS
val sign: String = if (i >= 0) {
"Positive"
} else {
"Negative"
}
36
EXPRESSIONS
val sign = if (i >= 0) {
"Positive"
} else {
"Negative"
}
37
EXPRESSIONS
val sign = if (i >= 0) "Positive" else "Negative"
38
EXPRESSIONS
val sign = if (i >= 0) "Positive" else “Negative"
String sign = i >= 0 ? "Positive" : “Negative" // Java
39
EXPRESSIONS
val evenNumbers = collection.mutable.ArrayBuffer[Int]()
for (i <- 1 to 10) evenNumbers.append(i * 2)
40
EXPRESSIONS
val evenNumbers = for (i <- 1 to 10) yield i * 2
val res = try callMethod() catch { case ex => defaultValue }


val avg = {

val sum = numbers.sum

sum / numbers.size

}

41
METHODS
42
METHODS
def add(a: Int, b: Int): Int = {

return a + b;

}
43
Keyword
Name Parameters Returntype
Body
METHODS
def add(a: Int, b: Int): Int = {

return a + b

}
44
Keyword
Name Parameters Returntype
Body
METHODS
def add(a: Int, b: Int): Int = {

a + b

}
45
Keyword
Name Parameters Returntype
Body
METHODS
def add(a: Int, b: Int): Int = a + b
46
Keyword
Name Parameters Returntype
Body
METHODS
def add(a: Int, b: Int) = a + b
47
Keyword
Name Parameters Returntype
Body
METHODS
def add(a: Int, b: Int) = a + b
48
METHODS
def add(a: Int, b: Int) = a + b
add(2, 2) // 4
49
NAMEDANDDEFAULTARGUMENTS
def resize(width: Int, height: Int, notify: Boolean = true)

50
NAMEDANDDEFAULTARGUMENTS
def resize(width: Int, height: Int, notify: Boolean = true)



resize(100, 200)

51
NAMEDANDDEFAULTARGUMENTS
def resize(width: Int, height: Int, notify: Boolean = true)



resize(100, 200)

resize(100, 200, false)
52
NAMEDANDDEFAULTARGUMENTS
def resize(width: Int, height: Int, notify: Boolean = true)



resize(100, 200)

resize(100, 200, false)
resize(100, 200, notify = false)
53
NAMEDANDDEFAULTARGUMENTS
def resize(width: Int, height: Int, notify: Boolean = true)



resize(100, 200)

resize(100, 200, false)
resize(100, 200, notify = false)
resize(width = 100, height = 200)

resize(height = 200, width = 100)

resize(height = 200, width = 100, notify = false)
54
FUNCTIONS
55
FUNCTIONS
• Functions are first class citizens
• Create, assign, apply
56
METHODS
def add(a: Int, b: Int) = a + b
57
FUNCTIONS
(a: Int, b: Int) => a + b
58
FUNCTIONS
val add = (a: Int, b: Int) => a + b
59
FUNCTIONS
val add = (a: Int, b: Int) => a + b // (Int, Int) => Int
60
FUNCTIONS
val add = new Function2[Int, Int, Int] {
def apply(a: Int, b: Int): Int = a + b
}
61
FUNCTIONS
val add = (a: Int, b: Int) => a + b
add.apply(2, 2) // 4
62
FUNCTIONS
val add = (a: Int, b: Int) => a + b
add(2, 2) // 4
63
PARTIALFUNCTIONS
64
PARTIALFUNCTIONS
val isEven: PartialFunction[Int, String] = {
case x if x % 2 == 0 => x + " is even"
}
isEven(2) // "2 is even"
isEven(3) // throws MatchError
65
PARTIALFUNCTIONS
val isEven: PartialFunction[Int, String] = {
case x if x % 2 == 0 => x + " is even"
}
isEven.isDefinedAt(2) // true
isEven.isDefinedAt(3) // false
66
EXERCISE2
67
COLLECTIONSI
COLLECTIONS
val numbers = List(17, 42, 314)



69
COLLECTIONS
val numbers = List(17, 42, 314)



numbers(1) // 42

70
COLLECTIONS
val numbers = List(17, 42, 314)



numbers(1) // 42

val clicks = Map("vg" -> 231232, "aftenposten" -> 158742)

71
COLLECTIONS
val numbers = List(17, 42, 314)



numbers(1) // 42

val clicks = Map("vg" -> 231232, "aftenposten" -> 158742)



clicks("vg") // 231232
72
COLLECTIONS
val numbers = List.apply(17, 42, 314)



numbers.apply(1) // 42

val clicks = Map.apply("vg" -> 231232, "aftenposten" -> 158742)



clicks.apply("vg”) // 231232
73
COLLECTIONS
• Collections ≈ partial functions
74
COLLECTIONOPERATIONS
75
CONSTRUCTION


val numbers = List(42, 314)



val moreNumbers = 17 :: numbers // List(17, 42, 314)
val fromScratch = 1 :: 2 :: 3 :: Nil // List(1, 2, 3)
76
HEADSANDTAILS


val numbers = List(17, 42, 314)



val first = numbers.head // 17
val rest = numbers.tail // List(42, 314)
77
SIZE


val names: List[String] = ...



val numberOfNames = names.size

78
SIZE


val names: List[String] = ...



val numberOfNames = names.length

79
EMPTINESS


val names: List[String] = ...



val empty = names.length == 0

80
EMPTINESS


val names: List[String] = ...



val empty = names.isEmpty

81
EMPTINESS


val names: List[String] = ...



val notEmpty = !names.isEmpty

82
EMPTINESS


val names: List[String] = ...



val notEmpty = names.nonEmpty

83
FILTERING


val names: List[String] = ...



val skywalkers = names.filter( ??? )

84
FILTERING


val names: List[String] = ...



val skywalkers = names.filter( ??? )

85
(A) => Boolean
FILTERING


val names: List[String] = ...



val skywalkers = names.filter( ??? )

86
(String) => Boolean
FILTERING


val names: List[String] = ...



val skywalkers = names.filter((name: String) => name.contains("Skywalker"))

87
FILTERING


val names: List[String] = ...



val skywalkers = names.filter(name => name.contains("Skywalker"))

88
FILTERING


val names: List[String] = ...



val skywalkers = names.filter(_.contains(“Skywalker"))

89
FILTERING


val names: List[String] = ...

val nameFilter: (String) => Boolean = _.contains(“Skywalker")

val skywalkers = names.filter(nameFilter)

90
FILTERING


val names: List[String] = ...

val nameFilter = (name: String) => name.contains("Skywalker")

val skywalkers = names.filter(nameFilter)

91
FILTERING


val names: List[String] = ...

def nameFilter(name: String) = name.contains("Skywalker")

val skywalkers = names.filter(nameFilter)

92
ITERATING


val names: List[String] = ...

names.foreach(name => println(name))

93
ITERATING


val names: List[String] = ...

names.foreach(println(_))

94
ITERATING


val names: List[String] = ...

names.foreach(println)

95
TRANSFORMING


val people: List[Person] = ...



val names = people.map( ??? )

96
(A) => B
TRANSFORMING


val people: List[Person] = ...



val names = people.map( ??? )

97
(Person) => B
TRANSFORMING


val people: List[Person] = ...



val names = people.map(person => person.name)

98
(Person) => String
TRANSFORMING


val people: List[Person] = ...



val names = people.map(person => person.name) // List[String]
99
(Person) => String
TRANSFORMING


val people: List[Person] = ...



val names = people.map(_.name) // List[String]
100
(Person) => String
SORTING


val names: List[String] = ...



val sortedNames = names.sorted

101
SORTING


val names: List[String] = ...



val reverseNames = names.sorted.reverse

102
SORTING


val names: List[String] = ...



val reverseNames = names.sorted(Ordering[String].reverse)
103
SORTING


val people: List[Person] = ...



val sortedByName = people.???

104
SORTING


val people: List[Person] = ...



val sortedByName = people.sortBy( ??? )

105
(A) => B // Ordering[B]
SORTING


val people: List[Person] = ...



val sortedByName = people.sortBy( ??? )

106
(Person) => B // Ordering[B]
SORTING


val people: List[Person] = ...



val sortedByName = people.sortBy(person => person.name)

107
(Person) => String // Ordering[String]
SORTING


val people: List[Person] = ...



val sortedByName = people.sortBy(_.name)

108
(Person) => String // Ordering[String]
SORTING


val people: List[Person] = ...



val reverseByName = people.sortBy(_.name)(Ordering[String].reverse)

109
AGGREGATING


val numbers = List(17, 42, 314)

val max = numbers.max // 314
val sum = numbers.sum // 373
110
COLLECTIONTYPES
• Sequences, Sets and Maps
• Immutable vs Mutable
• Performance
111
SEQUENCES
Immutable
• List
• Vector
• Queue
• Range
112
Mutable
• LinkedList
• ArrayBuffer / ListBuffer
• Queue
• ArrayStack
PERFORMANCE
113
Head Tail Length Lookup Update Prepend Append Insert
List 1 1 n n n 1 n -
Vector 1 * 1 * 1 * ? 1 * 1 * 1 * 1 * -
Queue 1 ** 1 ** n n n 1 1 -
ArrayBuffer 1 n 1 1 1 n 1 ** n
* = effectively ** = amortized
SETSANDMAPS
• HashSet
• TreeSet
• LinkedHashSet
• BitSet
114
• HashMap
• TreeMap
• LinkedHashMap
• IntMap / LongMap
COLLECTIONSAPI
• isEmpty, nonEmpty, size
• indexOf, contains, exists, forall, count
• head, tail, headOption, tailOption
• take, drop, takeWhile, dropWhile
• map, flatMap, foreach
• filter, filterNot, collect, partition
• sum, product, min, max, aggregate, fold
• reverse, sorted, sortBy, groupBy
• splitAt, grouped, sliding, combinations
115
MOREREADING
• http://docs.scala-lang.org/overviews/collections/overview.html
116
EXERCISE3
117
SCALAWORKSHOPKRAKOW
OCTOBER2017-DAY2
Fredrik Vraalsen - Data Platform
118
AGENDADAY2-FP
• Classes and Pattern Matching
• Collections II
• Laziness, implicits, DSLs
• QA & Wrapup
119
CLASSESANDPATTERNMATCHING
CLASSES
• Classes
• Traits
• Tuples
• Option
• Pattern matching
121
CLASSES
class Person(val name: String, var age: Int)
122
CLASSES
class Person(val name: String, var age: Int) {
// properties
// methods
// initialisation
}

123
CLASSES
package com.schibsted.model
import some.package.{ClassA, ClassB}
import some.other.package._
class Person(val name: String, var age: Int) {
// properties
// methods
// initialisation
}

124
CLASSES
class Person(val name: String, var age: Int)
val me = new Person("Fredrik", 41)

me.name // "Fredrik"
me.age // 41
me.age = 42
125
SINGLETONOBJECTS
object Person {

val constant = 42



def encode(user: Person): Json = ...

def decode(json: Json): Person = ...

def find(userId: Long): Person = ...

}
126
CASECLASSES
• Auto-generated methods
• Companion Object
127
CASECLASSES
case class Person(name: String, age: Int)

128
CASECLASSES
case class Person(name: String, age: Int)
class Person(val name: String, val age: Int) {

def copy(name: String, age: Int): Person

override def toString(): String

// hashCode, equals, etc.
}

129
CASECLASSES
case class Person(name: String, age: Int)
class Person(val name: String, val age: Int) {

def copy(name: String, age: Int): Person

override def toString(): String

// hashCode, equals, etc.
}



object Person {

def apply(name: String, age: Int): Person

// more
}
130
CASECLASSES
case class Person(name: String, age: Int)



val me = Person("Fredrik", 41)
val olderMe = me.copy(age = 42)
131
CASECLASSES
case class Person(name: String, age: Int)



val me = Person("Fredrik", 41)
val olderMe = me.copy(age = 42)
class Person(val name: String, val age: Int) {

def copy(name: String = this.name, age: Int = this.age): Person

...
}

132
INHERITANCE
• Single inheritance
133
INHERITANCE
abstract class Shape(val color: String) {
def area: Double
}
class Circle(val radius: Double, color: String) extends Shape(color) {
def area = radius * radius * Math.PI
}
val c = new Circle(1.0, "blue")
c.color // "blue"
c.area // 3.14159...
134
TRAITS
• Let’s you mix code into your class
• “Interfaces on steroids”
• Methods, values (abstract or implemented)
135
TRAITS
trait Ordered[T] {

def compare(that: T): Int



}
136
TRAITS
trait Ordered[T] {

def compare(that: T): Int



def < (that: A): Boolean = (this compare that) < 0
def > (that: A): Boolean = (this compare that) > 0
def <= (that: A): Boolean = (this compare that) <= 0
def >= (that: A): Boolean = (this compare that) >= 0
}
137
TRAITS
case class Person(name: String, age: Int)
extends Ordered[Person] {

override def compare(that: Person) = this.age - that.age
}
138
TRAITS
case class Person(name: String, age: Int)
extends Ordered[Person] {

override def compare(that: Person) = this.age - that.age
}
val a = Person("Fredrik", 41)

val b = Person("Johannes", 8)



a.>(b) // true

139
TRAITS
case class Person(name: String, age: Int)
extends Ordered[Person] {

override def compare(that: Person) = this.age - that.age
}
val a = Person("Fredrik", 41)

val b = Person("Johannes", 8)



a > b // true

140
TRAITS
case class Person(name: String, age: Int)
extends Ordered[Person] {

override def compare(that: Person) = this.age compare that.age
}
val a = Person("Fredrik", 41)

val b = Person("Johannes", 8)



a > b // true

141
MULTIPLETRAITS
case class Person(name: String, age: Int)
extends Ordered[Person]

with Cloneable
with Serializable {

override def compare(that: Person) = this.age compare that.age
}
142
TUPLES
• Unnamed structs
• Individually typed elements
143
TUPLES
val t1 = Tuple2("Fredrik", 41) // (String, Int)
val t2 = ("Fredrik", 41) // (String, Int)
t2._1 // "Fredrik"
t2._2 // 41
144
ADTS
• Algebraic Data Types
145
ADTS
sealed trait Tree
case class Node(left: Tree, right: Tree) extends Tree
case class Leaf(value: Int) extends Tree
val tree = Node(Node(Leaf(1), Leaf(2)), Leaf(3))
146
1 2
3
ADTS
sealed trait Tree[T]
case class Node[T](left: Tree[T], right: Tree[T]) extends Tree[T]
case class Leaf[T](value: T) extends Tree[T]
val tree = Node(Node(Leaf('a'), Leaf('b')), Leaf('c'))
147
a b
c
ADTS
sealed trait Json
case class JObject(fields: Map[String, Json]) extends Json
case class JArray(arr: List[Json]) extends Json
case class JString(str: String) extends Json
case class JNumber(num: BigDecimal) extends Json
case class JBool(value: Boolean) extends Json
case object JNull extends Json
val json = JObject(Map(
"name" -> JString("Fredrik"),
"age" -> JNumber(41)
))
148
ADTS
sealed trait Json
case class JObject(fields: Map[String, Json]) extends Json
case class JArray(arr: List[Json]) extends Json
case class JString(str: String) extends Json
case class JNumber(num: BigDecimal) extends Json
case class JBool(value: Boolean) extends Json
case object JNull extends Json
object JObject {
def apply(fields: (String, Json)*) = new JObject(fields.toMap)
}
val json = JObject(
"name" -> JString("Fredrik"),
"age" -> JNumber(41)
)
149
OPTIONALS
• Can contain an optional value of some type
• Transformations etc. work on the "happy path"
• Avoid null errors
150
OPTIONALS
val employees: Map[Int, Person] = …
employees(314)
151
OPTIONALS
val employees: Map[Int, Person] = …
employees.get(314) // Option[Person]
152
OPTIONALS
val employees: Map[Int, Person] = …
employees.get(314)
.map(_.name) // Option[String]
153
OPTIONALS
val employees: Map[Int, Person] = …
employees.get(314)
.map(_.name)
.getOrElse("Not found") // String
154
PATTERNMATCHING
• Values
• Types
• Structures
155
MATCHINGVALUES
val value = …
val result = value match {

case 1 => "number"

case "two" => "string"

case false => "boolean"

case _ => "unknown"

}



156
MATCHINGTYPES
val json = …
def jsonStr(json: Json) = json match {
case s: JString => '"' + s.str + '"'
case n: JNumber => n.num.toString
case b: JBool => b.value.toString
case JNull => "null"
}
val s = jsonStr(json)
157
MATCHINGSTRUCTURES
val json = …
val jsonStr = json match {
case JString(str) => '"' + str + '"'
case JNumber(num) => num.toString
case JBool(value) => value.toString
case JNull => "null"
}
158
MATCHINGSTRUCTURES
val tuple = …
val second = tuple match {

case (a, b) => b

}
159
MATCHINGSTRUCTURES
val tuple = …
val second = tuple match {

case (_, b) => b

}
160
MATCHINGSTRUCTURES
val tuple = …
val second = tuple match {

case ("special", b) => s"special $b"

case (_, b) => b

}
161
MATCHINGSTRUCTURES
val optional = …
val value = optional match {

case Some(value) => value

case None => defaultValue

}
162
MATCHINGSTRUCTURES
val optional = …
val value = optional match {

case Some(value) => value

case None => defaultValue

}
// optional.getOrElse(defaultValue)
163
MATCHINGSTRUCTURES
val list = …
val first = list match {

case head :: tail => head

case Nil => "Not found"

}
164
MATCHINGSTRUCTURES
val list = …
val value = list match {

case List(_, b) => b

case List(a, _*) => a

case Nil => "Not found"

}



165
MATCHINGREGULAREXPRESSIONS
val email = "(.*)@(.*..*)".r



"fredrik@vraalsen.no" match {

case email(user, domain) => s"User: $user, domain: $domain"

case _ => "Invalid email"

}

166
EXERCISE4
167
COLLECTIONSII
SUBLISTS


val numbers: List[Int] = List(1, 2, 3, 4, 5)



val (before, after) = numbers.splitAt(3) // (List(1, 2, 3), List(4, 5))

169
PARTITIONING


val people: List[Person] = ...



val adults = people.filter(_.age >= 18)

val kids = people.filterNot(_.age >= 18)

170
PARTITIONING


val people: List[Person] = ...



val (adults, kids) = people.partition(_.age >= 18)

171
SUBLISTS


val numbers: List[Int] = List(1, 2, 3, 4, 5)



val grouped = numbers.grouped(2) // List(1, 2), List(3, 4), List(5)

172
SUBLISTS


val numbers: List[Int] = List(1, 2, 3, 4, 5)



val grouped = numbers.sliding(3)
// List(1, 2, 3), List(2, 3, 4), List(3, 4, 5)

173
COMBINING


val numbers: List[Int] = List(17, 42)

val chars: List[Char] = List('a', 'b')



val combined = numbers.zip(chars) // List((17, 'a'), (42, 'b'))

174
COMBINING


val chars: List[Char] = List('a', 'b')



val indexed = chars.zipWithIndex // List(('a', 0), ('b', 1))

175
NESTEDDATA
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

176
NESTEDDATA
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

val filenames = getFiles(LocalDate.of(2016, 10, 10))



177
NESTEDDATA
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

val filenames = getFiles(LocalDate.of(2016, 10, 10))

val events = filenames.map(file => getEvents(file))



178
NESTEDDATA
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String



val filenames = getFiles(LocalDate.of(2016, 10, 10))

val events = filenames.map(file => getEvents(file)) // List[List[Json]]



179
NESTEDDATA
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

val filenames = getFiles(LocalDate.of(2016, 10, 10))

val events = filenames.map(file => getEvents(file)).flatten // List[Json]
180
FLATMAP
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

val filenames = getFiles(LocalDate.of(2016, 10, 10))

val events = filenames.flatMap(file => getEvents(file)) // List[Json]
181
FLATMAP
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

val filenames = getFiles(LocalDate.of(2016, 10, 10))

val events = filenames.flatMap(file => getEvents(file)) // List[Json]
val userIds = events.map(event => getUserId(event)) // List[String]
182
FLATMAP
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

val userIds = getFiles(LocalDate.of(2016, 10, 10))

.flatMap(file => getEvents(file)) // List[Json]
.map(event => getUserId(event)) // List[String]
183
FLATMAP
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

val userIds = getFiles(LocalDate.of(2016, 10, 10))

.flatMap(getEvents(_)) // List[Json]
.map(getUserId(_)) // List[String]
184
FLATMAP
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

val userIds = getFiles(LocalDate.of(2016, 10, 10))

.flatMap(getEvents) // List[Json]
.map(getUserId) // List[String]
185
MAP,FILTER&FLATMAP
• Reach into the container
• List, Option, Try, Future, …
• Take the happy path
186
FOR-COMPREHENSIONS
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

val userIds = getFiles(LocalDate.of(2016, 10, 10))

.flatMap(file => getEvents(file)) // List[Json]
.map(event => getUserId(event)) // List[String]
187
FOR-COMPREHENSIONS
def getFiles(date: LocalDate): List[String]

def getEvents(filename: String): List[Json]

def getUserId(event: Json): String

val userIds = getFiles(LocalDate.of(2016, 10, 10))

.flatMap(file => getEvents(file)) // List[Json]
.map(event => getUserId(event)) // List[String]
val userIds = for {

file <- getFiles(LocalDate.of(2016, 10, 10))

event <- getEvents(file)

} yield getUserId(file, event)

188
COLLECT
List result = new ArrayList();
for (Element element : elements) {
if (element instanceof SpecialElement) {
SpecialElement se = (SpecialElement) se;
if (accept(se))
result.add(transform(se));
}
}
189
https://ochafik.com/blog/?p=393
COLLECT
List result = new ArrayList();
for (Element element : elements) {
if (element instanceof SpecialElement) {
SpecialElement se = (SpecialElement) element;
if (accept(se))
result.add(transform(se));
}
}
val result = elements.collect {
case se: SpecialElement if accept(se) => transform(se)
}
190
https://ochafik.com/blog/?p=393
ACCUMULATION
def sum(numbers: List[Int])

191
ACCUMULATION
def sum(numbers: List[Int]) = {

var sum = 0

for (i <- numbers) sum += i

sum

}

192
RECURSION
def sum(numbers: List[Int]): Int = numbers match {

case Nil => 0

case head :: tail => head + sum(tail)

}

193
RECURSION
@tailrec

def sum(numbers: List[Int]): Int = numbers match {

case Nil => 0

case head :: tail => head + sum(tail)

}



194
RECURSION
def sum(numbers: List[Int]) = {

@tailrec

def loop(acc: Int, numbers: List[Int]): Int = numbers match {

case Nil => acc

case head :: tail => loop(acc + head, tail)

}



loop(0, numbers)

}

195
RECURSION-THEGOTOOFFP
196
FOLD
197
FOLD
• Initial value
• Accumulator
• Operator
198
FOLD
def sum(numbers: List[Int]) = {

@tailrec

def loop(acc: Int, numbers: List[Int]): Int = numbers match {

case Nil => acc

case head :: tail => loop(acc + head, tail)

}



loop(0, numbers)

}

199
FOLD
def sum(numbers: List[Int]) = {

@tailrec

def loop(acc: Int, numbers: List[Int]): Int = numbers match {

case Nil => acc

case head :: tail => loop(acc + head, tail)

}



loop(0, numbers)

}



def sum(numbers: List[Int]) = numbers.foldLeft(0)((acc, n) => acc + n)
200
FOLD
def sum(numbers: List[Int]) = {

@tailrec

def loop(acc: Int, numbers: List[Int]): Int = numbers match {

case Nil => acc

case head :: tail => loop(acc + head, tail)

}



loop(0, numbers)

}



def sum(numbers: List[Int]) = numbers.foldLeft(0)(_ + _)
201
EXERCISE5
202
LAZINESS,IMPLICITSANDDSLS
LAZINESS
• Streams
• Iterators
• Views
204
STREAMS
• Lazy sequence
• Values computed on demand first time
• Can be accessed many times
205
ITERATORS
• Iterate over elements one by one
• Use once
206
VIEWS
• Lazy transformations
• map, filter, etc.
• Use once
207
IMPLICITS
• Values / parameters / methods automagically inserted by compiler
208
IMPLICITS


val people: List[Person] = ...



val sortByName = people.sortBy(_.name)

209
IMPLICITS


val people: List[Person] = ...



val sortByName = people.sortBy(_.name)
def sortBy[B](f: A => B): List[B]
210
IMPLICITS


val people: List[Person] = ...



val sortByName = people.sortBy(_.name)
def sortBy[B](f: A => B)(implicit ord: scala.math.Ordering[B]): List[B]
211
IMPLICITS


val people: List[Person] = ...



val sortByName = people.sortBy(_.name)(Ordering[String])
def sortBy[B](f: A => B)(implicit ord: scala.math.Ordering[B]): List[B]
212
IMPLICITS
val clicks = Map("vg" -> 231232, "aftenposten" -> 158742)

213
IMPLICITS
val clicks = Map("vg" -> 231232, "aftenposten" -> 158742)

val clicks = Map(Tuple2(“vg”, 231232), Tuple2("aftenposten", 158742))

214
IMPLICITS
val clicks = Map("vg" -> 231232, "aftenposten" -> 158742)

val clicks = Map(Tuple2(“vg”, 231232), Tuple2("aftenposten", 158742))

implicit class ArrowAssoc[A](self: A) extends AnyVal {
def -> [B](b: B): Tuple2[A, B] = Tuple2(self, b)
}
215
DSLS
• Domain Specific Languages
• Decorators
• "Pimp my library"
• Jackson
• Kafka Streams
216
REPLACINGJSON4S
def providerId(event: JValue) = event  "provider"  "@id"
217
JACKSON
def providerId(event: JsonNode) = event.get("provider").get("@id")
218
JACKSON
implicit class RichJsonNode(node: JsonNode) {
def (fieldName: String): JsonNode =
Option(node get fieldName) getOrElse NullNode.instance
}
def providerId(event: JsonNode) = event  "provider"  "@id"
219
JACKSON
implicit class RichJsonNode(node: JsonNode) {
def (fieldName: String): JsonNode =
node path fieldName
}
def providerId(event: JsonNode) = event  "provider"  "@id"
220
221
KAFKASTREAMS
val events = builder.stream(strings, json, sourceTopic)
val transformed = events.filter( … ).map( … )
transformed.to(strings, json, resultTopic)
222
KAFKASTREAMS
val events = builder.stream(strings, json, sourceTopic)
val transformed = events.filter( … ).map( … )
transformed.to(resultTopic) // ???
223
KAFKASTREAMS
class KStream[K, V] {
def to(keySerde: Serde[K], valSerde: Serde[V], topic: TopicName)
}
224
KAFKASTREAMS
object KafkaStreamsDSL {
implicit class RichKStream[K, V](stream: KStream[K, V]) {
def to(topic: TopicName)
}
}
225
KAFKASTREAMS
object KafkaStreamsDSL {
implicit class RichKStream[K, V](stream: KStream[K, V]) {
def to(topic: TopicName): Unit =
stream.to(keySerde, valSerde, topic)
}
}
226
KAFKASTREAMS
object KafkaStreamsDSL {
implicit class RichKStream[K, V](stream: KStream[K, V]) {
def to(topic: TopicName)
(implicit keySerde: Serde[K], valSerde: Serde[V]): Unit =
stream.to(keySerde, valSerde, topic)
}
}
227
KAFKASTREAMS
val events = builder.stream(strings, json, sourceTopic)
val transformed = events.filter( … ).map( … )
transformed.to(resultTopic) // ???
228
KAFKASTREAMS
val events = builder.stream(strings, json, sourceTopic)
val transformed = events.filter( … ).map( … )
import KafkaStreamsDSL._
implicit val strings = Serdes.string()
implicit val json = new JsonNodeSerde
transformed.to(resultTopic)
229
KAFKASTREAMS
val events = builder.stream(strings, json, sourceTopic)
val transformed = events.filter( … ).map( … )
import KafkaStreamsDSL._
implicit val strings = Serdes.string()
implicit val json = new JsonNodeSerde
transformed.~>(resultTopic)
230
KAFKASTREAMS
val events = builder.stream(strings, json, sourceTopic)
val transformed = events.filter( … ).map( … )
import KafkaStreamsDSL._
implicit val strings = Serdes.string()
implicit val json = new JsonNodeSerde
transformed ~> resultTopic
231
EVOLUTION
232
EVOLUTION
def extractUsers(iam: AmazonIdentityManagementClient, marker: String):
Seq[JsObject] = {
val result = iam.listUsers(new ListUsersRequest().withMarker(marker))
val batch = result.getUsers().asScala.map(userJson(iam, _))
val next: Seq[JsObject] =
if (result.getMarker() != null) {
extractUsers(iam, result.getMarker())
} else {
Seq[JsObject]()
}
batch ++ next
}
233
EVOLUTION
def extractGroups(iam: AmazonIdentityManagementClient, marker: String):
Seq[JsObject] = {
val result = iam.listGroups(new ListGroupsRequest().withMarker(marker))
val batch = result.getGroups().asScala.map(groupJson(_))
val next: Seq[JsObject] =
if (result.getMarker() != null) {
extractGroups(iam, result.getMarker())
} else {
Seq[JsObject]()
}
batch ++ next
}
234
EVOLUTION
def extract[A](fetch: String => A,
convert: A => Seq[JsObject],
isDone: A => Boolean,
getMarker: A => String): Seq[JsObject] = {
def loop(marker: String): Seq[JsObject] = {
val result = fetch(marker)
val batch = convert(result)
if (isDone(result)) batch else batch ++ loop(getMarker(result))
}
loop(null)
}
235
EVOLUTION
def extractUsers(iam: AmazonIdentityManagement) =
extract[ListUsersResult](
marker => iam.listUsers(new ListUsersRequest().withMarker(marker)),
result => result.getUsers.asScala.map(userJson(iam, _)),
_.isTruncated,
_.getMarker
)
def extractGroups(iam: AmazonIdentityManagement) =
extract[ListGroupsResult](
marker => iam.listGroups(new ListGroupsRequest().withMarker(marker)),
result => result.getGroups.asScala.map(groupJson),
_.isTruncated,
_.getMarker
)
236
EVOLUTION
type AwsListResult = {
def isTruncated(): java.lang.Boolean
def getMarker(): String
}
237
EVOLUTION
def extract[A](fetch: String => A,
convert: A => Seq[JsObject],
isDone: A => Boolean,
getMarker: A => String): Seq[JsObject] = {
def loop(marker: String): Seq[JsObject] = {
val result = fetch(marker)
val batch = convert(result)
if (isDone(result)) batch else batch ++ loop(getMarker(result))
}
loop(null)
}
238
EVOLUTION
def extract[A <: AwsListResult](fetch: String => A,
convert: A => Seq[JsObject]): Seq[JsObject] = {
def loop(marker: String): Seq[JsObject] = {
val result = fetch(marker)
val batch = convert(result)
if (result.isTruncated()) batch ++ loop(result.getMarker()) else batch
}
loop(null)
}
239
EVOLUTION
def extractUsers(iam: AmazonIdentityManagement) =
extract(
marker => iam.listUsers(new ListUsersRequest().withMarker(marker)),
result => result.getUsers.asScala.map(userJson(iam, _))
)
def extractGroups(iam: AmazonIdentityManagement) =
extract(
marker => iam.listGroups(new ListGroupsRequest().withMarker(marker)),
result => result.getGroups.asScala.map(groupJson)
)
240
EVOLUTION
def extract[A <: AwsListResult](fetch: String => A,
convert: A => Seq[JsObject]): Seq[JsObject] = {
def loop(marker: String): Seq[JsObject] = {
val result = fetch(marker)
val batch = convert(result)
if (result.isTruncated()) batch ++ loop(result.getMarker()) else batch
}
loop(null)
}
241
EVOLUTION
def extract[A <: AwsListResult, B](fetch: String => A)
(extract: A => java.util.List[B])
(implicit toJson: B => JsObject): Seq[JsObject] =
def loop(marker: String): Seq[JsObject] = {
val result = fetch(marker)
val batch = extract(result).asScala.map(toJson)
if (result.isTruncated()) batch ++ loop(result.getMarker()) else batch
}
loop(null)
}
242
EVOLUTION
def extractUsers() = extract(
marker => iam.listUsers(new ListUsersRequest().withMarker(marker))
)(_.getUsers)
def extractGroups() = extract(
marker => iam.listGroups(new ListGroupsRequest().withMarker(marker))
)(_.getGroups)
243
EVOLUTION
def extractUsers() = extract(users)(_.getUsers)
def extractGroups() = extract(groups)(_.getGroups)
def users(marker: String) =
iam.listUsers(new ListUsersRequest().withMarker(marker))
def groups(marker: String) =
iam.listGroups(new ListGroupsRequest().withMarker(marker))
244
ASYNC
245
ASYNC
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): List[Json]

246
ASYNC
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): List[Json]



val content = getFile(LocalDate.of(2016, 10, 10))
val events = ???
247
ASYNC
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): List[Json]



val content = getFile(LocalDate.of(2016, 10, 10))

content.onComplete {


}

248
ASYNC
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): List[Json]



val content = getFile(LocalDate.of(2016, 10, 10))

content.onComplete { res =>
res match {

case Success(json) => doSomething(json)

case Failure(ex) => fallbackLogic(ex)
}

}

249
ASYNC
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): List[Json]



val content = getFile(LocalDate.of(2016, 10, 10))

content.onComplete {

case Success(json) => doSomething(json)

case Failure(ex) => fallbackLogic(ex)

}

250
ASYNC
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): List[Json]



val content = getFile(LocalDate.of(2016, 10, 10))

content.onComplete {

case Success(json) => doSomething(json)

case Failure(ex) => fallbackLogic(ex)

}

val events = ???
251
MAP
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): List[Json]



val content = getFile(LocalDate.of(2016, 10, 10))
val events = content.map(json => parseEvents(json))
252
MAP
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): List[Json]



val content = getFile(LocalDate.of(2016, 10, 10))
val events = content.map(json => parseEvents(json)) // Future[List[Json]]
253
MAP
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): List[Json]



val content = getFile(LocalDate.of(2016, 10, 10))
val events = content.map(json => parseEvents(json)) // Future[List[Json]]
Await.result(events, Duration.Inf)
254
MAP
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): Future[List[Json]]



val content = getFile(LocalDate.of(2016, 10, 10))
val events = content.map(json => parseEvents(json))
// Future[Future[List[Json]]]
255
FLATMAP
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): Future[List[Json]]



val content = getFile(LocalDate.of(2016, 10, 10))
val events = content.flatMap(json => parseEvents(json)) // Future[List[Json]]
256
FOR-COMPREHENSIONS
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): Future[List[Json]]



val content = getFile(LocalDate.of(2016, 10, 10))
val events = content.flatMap(json => parseEvents(json))
257
FOR-COMPREHENSIONS
def getFile(date: LocalDate): Future[String]

def parseEvents(json: String): Future[List[Json]]



val content = getFile(LocalDate.of(2016, 10, 10))
val events = content.flatMap(json => parseEvents(json))


val events = for {

json <- getFile(LocalDate.of(2016, 10, 10))

events <- parseEvents(json)

} yield events



258
PARALLELLISM
• Futures for-comprehension (flatMap) vs zip/zipWith vs sequence
• traverse (parallel map)
259
WRAPUPANDQA
IMPERATIVEVSFUNCTIONAL
• Imperative : Change state
• Functional Programming (FP) : Compute new values
261
OBJECT-ORIENTEDVSFUNCTIONAL
• OO: Objects = state + behavior
• FP: Data structures + functions
262
OO+FP
• Scala = Hybrid FP + OO
• Leans towards FP “in the small”
• OO for “programming in the large”
263
DON’TBECUTE
• Optimize for readability
• Find the Scala level that works for your team
• The language grows with your understanding
• of Scala, and of FP
264
QA
• Questions?
• Anything you want to dive deeper into?
265
THANKYOU!
@fredriv / fredrik.vraalsen@schibsted.com

More Related Content

What's hot (20)

PDF
Scala for Jedi
Vladimir Parfinenko
 
PPTX
Intro to Functional Programming in Scala
Shai Yallin
 
KEY
Scala for ruby programmers
tymon Tobolski
 
PDF
Scalaz 8: A Whole New Game
John De Goes
 
PPTX
Practically Functional
djspiewak
 
PDF
Type classes 101 - classification beyond inheritance
Alexey Raga
 
ODP
JavaScript Web Development
vito jeng
 
PDF
Scala vs Java 8 in a Java 8 World
BTI360
 
PDF
Refactoring Functional Type Classes
John De Goes
 
PDF
Scala introduction
vito jeng
 
PDF
High Wizardry in the Land of Scala
djspiewak
 
PDF
Get started with Reason
Nikolaus Graf
 
PDF
Ankara Jug - Practical Functional Programming with Scala
Ensar Basri Kahveci
 
PDF
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Philip Schwarz
 
PDF
Scala by Luc Duponcheel
Stephan Janssen
 
PDF
The Death of Final Tagless
John De Goes
 
PDF
Scala for Java Programmers
Eric Pederson
 
PPT
En nybegynners introduksjon til scalaz
Trond Marius Øvstetun
 
PDF
Exploring ZIO Prelude: The game changer for typeclasses in Scala
Jorge Vásquez
 
PDF
First-Class Patterns
John De Goes
 
Scala for Jedi
Vladimir Parfinenko
 
Intro to Functional Programming in Scala
Shai Yallin
 
Scala for ruby programmers
tymon Tobolski
 
Scalaz 8: A Whole New Game
John De Goes
 
Practically Functional
djspiewak
 
Type classes 101 - classification beyond inheritance
Alexey Raga
 
JavaScript Web Development
vito jeng
 
Scala vs Java 8 in a Java 8 World
BTI360
 
Refactoring Functional Type Classes
John De Goes
 
Scala introduction
vito jeng
 
High Wizardry in the Land of Scala
djspiewak
 
Get started with Reason
Nikolaus Graf
 
Ankara Jug - Practical Functional Programming with Scala
Ensar Basri Kahveci
 
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
Philip Schwarz
 
Scala by Luc Duponcheel
Stephan Janssen
 
The Death of Final Tagless
John De Goes
 
Scala for Java Programmers
Eric Pederson
 
En nybegynners introduksjon til scalaz
Trond Marius Øvstetun
 
Exploring ZIO Prelude: The game changer for typeclasses in Scala
Jorge Vásquez
 
First-Class Patterns
John De Goes
 

Similar to Scala intro workshop (20)

PDF
Meet scala
Wojciech Pituła
 
ODP
Scala ntnu
Alf Kristian Støyle
 
PDF
Scala Quick Introduction
Damian Jureczko
 
PDF
Scala or functional programming from a python developer's perspective
gabalese
 
PDF
Demystifying functional programming with Scala
Denis
 
PDF
An Introduction to Scala (2014)
William Narmontas
 
PDF
Spark workshop
Wojciech Pituła
 
PPTX
Scala Introduction
Constantine Nosovsky
 
PDF
Lecture 5: Functional Programming
Eelco Visser
 
PPT
An introduction to scala
Mohsen Zainalpour
 
PDF
Scala 101
Andrey Myatlyuk
 
PDF
여자개발자모임터 6주년 개발 세미나 - Scala Language
Ashal aka JOKER
 
PDF
Functional programming in Scala
Damian Jureczko
 
PDF
Scala for Java Developers (Silicon Valley Code Camp 13)
Ramnivas Laddad
 
PDF
Scala for Java Developers - Intro
David Copeland
 
PDF
Scala Hands On!!
Yoshifumi Takeshima
 
PDF
Getting Started With Scala
Meetu Maltiar
 
PDF
Scala
Sven Efftinge
 
PPTX
Scala Up North: "Analysing Scala Puzzlers: Essential and Accidental Complexit...
Andrew Phillips
 
PPTX
BASE Meetup: "Analysing Scala Puzzlers: Essential and Accidental Complexity i...
Andrew Phillips
 
Meet scala
Wojciech Pituła
 
Scala Quick Introduction
Damian Jureczko
 
Scala or functional programming from a python developer's perspective
gabalese
 
Demystifying functional programming with Scala
Denis
 
An Introduction to Scala (2014)
William Narmontas
 
Spark workshop
Wojciech Pituła
 
Scala Introduction
Constantine Nosovsky
 
Lecture 5: Functional Programming
Eelco Visser
 
An introduction to scala
Mohsen Zainalpour
 
Scala 101
Andrey Myatlyuk
 
여자개발자모임터 6주년 개발 세미나 - Scala Language
Ashal aka JOKER
 
Functional programming in Scala
Damian Jureczko
 
Scala for Java Developers (Silicon Valley Code Camp 13)
Ramnivas Laddad
 
Scala for Java Developers - Intro
David Copeland
 
Scala Hands On!!
Yoshifumi Takeshima
 
Getting Started With Scala
Meetu Maltiar
 
Scala Up North: "Analysing Scala Puzzlers: Essential and Accidental Complexit...
Andrew Phillips
 
BASE Meetup: "Analysing Scala Puzzlers: Essential and Accidental Complexity i...
Andrew Phillips
 
Ad

More from Fredrik Vraalsen (10)

PDF
Building applications with Serverless Framework and AWS Lambda - JavaZone 2019
Fredrik Vraalsen
 
PDF
Building applications with Serverless Framework and AWS Lambda
Fredrik Vraalsen
 
PDF
Kafka and Kafka Streams in the Global Schibsted Data Platform
Fredrik Vraalsen
 
PDF
Event stream processing using Kafka streams
Fredrik Vraalsen
 
PDF
Hjelp, vi skal kode funksjonelt i Java!
Fredrik Vraalsen
 
PDF
Java 8 DOs and DON'Ts - javaBin Oslo May 2015
Fredrik Vraalsen
 
PDF
Functional programming in Java 8 - workshop at flatMap Oslo 2014
Fredrik Vraalsen
 
PDF
Java 8 - Return of the Java
Fredrik Vraalsen
 
PDF
Java 8 to the rescue!?
Fredrik Vraalsen
 
ODP
Git i praksis - erfaringer med overgang fra ClearCase til Git
Fredrik Vraalsen
 
Building applications with Serverless Framework and AWS Lambda - JavaZone 2019
Fredrik Vraalsen
 
Building applications with Serverless Framework and AWS Lambda
Fredrik Vraalsen
 
Kafka and Kafka Streams in the Global Schibsted Data Platform
Fredrik Vraalsen
 
Event stream processing using Kafka streams
Fredrik Vraalsen
 
Hjelp, vi skal kode funksjonelt i Java!
Fredrik Vraalsen
 
Java 8 DOs and DON'Ts - javaBin Oslo May 2015
Fredrik Vraalsen
 
Functional programming in Java 8 - workshop at flatMap Oslo 2014
Fredrik Vraalsen
 
Java 8 - Return of the Java
Fredrik Vraalsen
 
Java 8 to the rescue!?
Fredrik Vraalsen
 
Git i praksis - erfaringer med overgang fra ClearCase til Git
Fredrik Vraalsen
 
Ad

Recently uploaded (20)

PDF
SAP GUI Installation Guide for Windows | Step-by-Step Setup for SAP Access
SAP Vista, an A L T Z E N Company
 
PDF
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
PDF
Salesforce Implementation Services Provider.pdf
VALiNTRY360
 
PPTX
Employee salary prediction using Machine learning Project template.ppt
bhanuk27082004
 
PDF
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
PDF
Enhancing Security in VAST: Towards Static Vulnerability Scanning
ESUG
 
PDF
Why Are More Businesses Choosing Partners Over Freelancers for Salesforce.pdf
Cymetrix Software
 
PPTX
Presentation about Database and Database Administrator
abhishekchauhan86963
 
PDF
Supabase Meetup: Build in a weekend, scale to millions
Carlo Gilmar Padilla Santana
 
PDF
What companies do with Pharo (ESUG 2025)
ESUG
 
PDF
Step-by-Step Guide to Install SAP HANA Studio | Complete Installation Tutoria...
SAP Vista, an A L T Z E N Company
 
PPTX
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
PDF
WatchTraderHub - Watch Dealer software with inventory management and multi-ch...
WatchDealer Pavel
 
PDF
System Center 2025 vs. 2022; What’s new, what’s next_PDF.pdf
Q-Advise
 
PDF
Protecting the Digital World Cyber Securit
dnthakkar16
 
PDF
How Agentic AI Networks are Revolutionizing Collaborative AI Ecosystems in 2025
ronakdubey419
 
PDF
Infrastructure planning and resilience - Keith Hastings.pptx.pdf
Safe Software
 
PDF
AWS_Agentic_AI_in_Indian_BFSI_A_Strategic_Blueprint_for_Customer.pdf
siddharthnetsavvies
 
PDF
MiniTool Power Data Recovery Crack New Pre Activated Version Latest 2025
imang66g
 
PDF
Virtual Threads in Java: A New Dimension of Scalability and Performance
Tier1 app
 
SAP GUI Installation Guide for Windows | Step-by-Step Setup for SAP Access
SAP Vista, an A L T Z E N Company
 
Balancing Resource Capacity and Workloads with OnePlan – Avoid Overloading Te...
OnePlan Solutions
 
Salesforce Implementation Services Provider.pdf
VALiNTRY360
 
Employee salary prediction using Machine learning Project template.ppt
bhanuk27082004
 
Enhancing Healthcare RPM Platforms with Contextual AI Integration
Cadabra Studio
 
Enhancing Security in VAST: Towards Static Vulnerability Scanning
ESUG
 
Why Are More Businesses Choosing Partners Over Freelancers for Salesforce.pdf
Cymetrix Software
 
Presentation about Database and Database Administrator
abhishekchauhan86963
 
Supabase Meetup: Build in a weekend, scale to millions
Carlo Gilmar Padilla Santana
 
What companies do with Pharo (ESUG 2025)
ESUG
 
Step-by-Step Guide to Install SAP HANA Studio | Complete Installation Tutoria...
SAP Vista, an A L T Z E N Company
 
classification of computer and basic part of digital computer
ravisinghrajpurohit3
 
WatchTraderHub - Watch Dealer software with inventory management and multi-ch...
WatchDealer Pavel
 
System Center 2025 vs. 2022; What’s new, what’s next_PDF.pdf
Q-Advise
 
Protecting the Digital World Cyber Securit
dnthakkar16
 
How Agentic AI Networks are Revolutionizing Collaborative AI Ecosystems in 2025
ronakdubey419
 
Infrastructure planning and resilience - Keith Hastings.pptx.pdf
Safe Software
 
AWS_Agentic_AI_in_Indian_BFSI_A_Strategic_Blueprint_for_Customer.pdf
siddharthnetsavvies
 
MiniTool Power Data Recovery Crack New Pre Activated Version Latest 2025
imang66g
 
Virtual Threads in Java: A New Dimension of Scalability and Performance
Tier1 app
 

Scala intro workshop