Upload
fero-kocun
View
2.722
Download
1
Tags:
Embed Size (px)
Citation preview
Scala
František Kocun
“I can honestly say if someone had shown me the Programming in Scala book by Martin Odersky, Lex Spoon & Bill Venners back in 2003 I'd probably have never created Groovy.“
James Strachan, Groovy creator
“No other language on the JVM seems as capable of being a "replacement for Java" as Scala, and the momentum behind Scala is now unquestionable.“
Charles Nutter, JRuby core developer
“Scala.“James Gosling, Java creator
Answer on a question, which language would he use now on JVM except Java.
“On my radar, the current best exit strategy for Java is Scala.„
Bruce Eckel, author of many books
Scala vs Java
Scala removes• break, continue, goto• static• primitive types• raw types (every type parameter „generics“ has to be
stated)• operators (all are methods)• override is not more optional
(when overriding in Scala one has to declare override)
Scala vs Ruby and Groovy• Scala has two type of fans
– Java programmers (pros of functional programming)– Ruby programmers (pros of language with static typing)
• Implicits, structural typing and type inference, resembles work with language with dynamic typing
• Scala is compiles a compiled language and all types are checked in compilation time– one can not call a non existing method– no ClassCastException in runtime– possibility to refactor– as fast as Java
Scala unifies FP and OOP
Why to unify FP and OOP
Functional
• Abstraction– Functions– Higher-order functions– Parametric
polymorphism
Object-oriented
• Extending, state dependent behavior– Inheritance– Polymorphism– Encapsulation– Dynamic configuration
Both approaches are complementary
Where?
• Manipulation of data structures – collections, trees, ADTs, ...
• Internal/External DSLs– OSGi, Apache Camel, scala-query,
specs, built-in parser
• Modular applications– Mixins, dependency injection, DCI
(data, context & interaction)
How?
•Function as input argument•Pattern matching, case classes•Parametric polymorphism
•Traits•Higher-order functions
•Function as output argument•Implicits
• Manipulation of data structures – collections, trees, ADTs, ...
• Internal/External DSLs– OSGi, Apache Camel, scala-query,
specs, built-in parser
• Modular applications– Mixins, dependency injection, DCI
(data, context & interaction)
Function as value
Manipulation of data structuresCollectionsTreesDSL
Example - filtering
Javapublic Collection<Ticket> approved(Collection<Ticket> tickets){
Collection<Ticket> result = new ArrayList<Ticket>();for(Ticket ticket : tickets){
if(ticket.getState() == State.APPROVED)result.add(icket);
}return result;
}
Example - filtering
Javapublic Collection<Ticket> approved(Collection<Ticket> tickets){
Collection<Ticket> result = new ArrayList<Ticket>();for(Ticket ticket : tickets){ filter
if(ticket.getState() == State.APPROVED)result.add(ticket);
}return result;
}
With what?
What to do?
How?
Yes, it can be done in Javapublic interface ICondition<T> {
public boolean check(T toBeChecked);}
Interface for conditon
... and the use
Filter util method
public static <T> Collection<T> filter(Collection<T> ts, ICondition<T> c){Collection<T> result = new ArrayList<T>();for(T t : ts)
if(c.check(t)) result.add(t);return result;
}
CollestionUtils.filter(verzie, new ICondition<Ticket>() {public boolean check(Ticket toBeChecked){return toBeChecked.getState() == State.APPROVED;}
});
Example - filtering
Scala
def approved(tickets: List[Ticket]) {tickets filter (_.getState() == State.APPROVED)
}
WTF ?
What type has the parameter in the function „filter“?
List[+A] {def filter (p : (A) => Boolean) : List[A]
}
Java : (A) => Booleanpublic interface Function1<O, I> {
O apply(I input);}
interface ICondition<I> extends IFunction1<Boolean, I> {}
Scala List
count (p : (A) => Boolean) : Intexists (p : (A) => Boolean) : Booleanfilter (p : (A) => Boolean) : List[A] find (p : (A) => Boolean) : Option[A] foldLeft [B](z : B)(f : (B, A) => B) : Bforall (p : (A) => Boolean) : Booleanforeach (f : (A) => Unit) : Unitmap [B](f : (A) => B) : List[B] remove (p : (A) => Boolean) : List[A]
Scala List
count (p : (A) => Boolean) : Intexists (p : (A) => Boolean) : Booleanfilter (p : (A) => Boolean) : List[A] find (p : (A) => Boolean) : Option[A] foldLeft [B](z : B)(f : (B, A) => B) : Bforall (p : (A) => Boolean) : Booleanforeach (f : (A) => Unit) : Unitmap [B](f : (A) => B) : List[B] remove (p : (A) => Boolean) : List[A]
Most of them is inherited from trait Iterable. So every collection, array has them, because they inherit from Iterable.
Similar functions can be written for trees.
Function can be assigned to variable
var f : (String => Unit) = (x) => println(x)
Function can be passed to another function
tickets filter (t => t.getState() == State.APPROVED)
Return value from function can be a function
def deductKind = tax | insurance | retirement
Function is primitive type, basic building block, with the same importance as an integer or a string.
If functions takes another function as an argument or if it returns a function, it is called a higher-order function.
ExamplesList(1, 2, 3, 4, 5) map { _ * 2 }> List[Int] = List(2, 4, 6, 8, 10)
case class Employee( var name : String)case class DTOEmployee( var name : String)
def toDto(e : Employee) = new DTOZamestnanec(z.meno)
List(new Employee(“John"), new Employee(“Jack")) map toDto> List[DTOEmployee] = List(DTOEmployee(John),
DTOEmployee(Jack))
Numbers multiplied by two
Employees to DTOs
Type declaration
In Scala types are written after colon
exists (p : (A) => Boolean) : Boolean
var age : Int = 24 val age = 33
Return type of function exists
Argument p is of type function from A to Boolean
Type of variable age
If type is unambiguous, it doesn’t have to be declared
Val vs Var• immutable (v Java it would be final)• creates field and getter
val age:Int=22age = 33 //ERROR
• variable• creates field, getter and setter
var age:Int=22age = 33
List
val list1 = List("Programming", "Scala") val list2 = "People" :: "should" :: "read" :: list1
val list2 = ("People" :: ("should" :: ("read" :: list1))) val list2 = list1.::("read").::("should").::("People")
Operator :: is bound from right
list2
Mapval stateCapitals = Map( "Alabama" -> "Montgomery", "Alaska" -> "Juneau", "Wyoming" -> "Cheyenne")
val lengths = stateCapitals map { kv => (kv._1, kv._2.length)}
println(lengths)
> Map(Alabama -> 10, Alaska -> 6, Wyoming -> 8)
State -> Capitalis mapped toState -> length of the capital name
FoldLeft
def foldLeft[B](z: B)(op: (B, A) => B): B = this match { case Nil => z case x :: xs => (xs foldLeft op(z, x))(op)}
FoldLeftList(1,2,3,4,5).foldLeft(10)(_ * _)(((((10 * 1) * 2) * 3) * 4) * 5)
List(1,2,3,4,5).foldLeft(10)( (x, y) => x +y)(((((10 + 1) + 2) + 3) + 4) + 5)
Shortcut for arguments of anonymous functions. First underscore means the first argument, second the second…
QuestionsHow would we write function “sum” with the help of foldLeft?
How would we write function “distinct“with the help of foldLeft? (Selects only distinct elements form the list.)
def sumInt(l : List[Int]) : Int = l.foldLeft(0)(_ + _)
def distinct[A](l : List[A]) : List[A] = (l foldLeft List[A]()) {(res, a)=> if (!res.contains(a)) a :: res else res}
Every value is an objectEvery operator a method
factorial (x - 1)factorial (x.-(1))
map.containsKey(‘a’)map containsKey ‘a’
There are no operators in Scala. Method names can contain some symbols.
Every method can be called on the object without the dot and the brackets.
There are no operators in Scala. Method names can contain some symbols.
trait Ordered[A] extends java.lang.Comparable[A] {
def compare(that: A): 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 def compareTo(that: A): Int = compare(that)}
“While” doesn’t have to be a keyword... But it is...
def whileAwesome(conditional: => Boolean)(f: => Unit) { if (conditional) { f whileAwesome(conditional)(f) }}
var count = 0whileAwesome(count < 5) { println("still awesome") count += 1}
>still awesome still awesome still awesome still awesome still awesome
Conditional is of type (=> Boolean) , so it is evaluated in the function “whileAwesome”
If conditional was of type Boolean, it would be evaluated before calling the function so the loop would never stop writing "still awesome"
It has many real life uses
tx { val person = new Person(id=null, name= "Ivanka"); manager.persist(person) }
def tx(f: => Unit) = { manager.getTransaction().begin() try { tx manager.getTransaction().commit() } catch { case e: Exception => manager.getTransaction().rollback() }}
Functions are objects too
trait Function1[-S, +T] { def apply(x: S): T}
E.g. anonymous function (x: Int ) => x + 1 is translated by the compiler to
new Function1[Int, Int] { def apply(x: Int): Int = x + 1}
Functions are objects too
Array[T] is a function Int => T, so if such function is needed we can pass an Array[T]
class Array [T] ( length: Int ) extends (Int => T) { def length: Int = ... def apply(i: Int): T = ... def update(i: Int, x: T): Unit = ... def elements: Iterator[T] = ... def exists(p: T => Boolean):Boolean = ...}
While (=>) is a class, it can be inherited from it.So we can specialize the concept of a function.
Instead ofa(i) = a(i) + 2we can writea.update(i, a.apply(i) + 2)
Currying
Partial function application
CurryingFunction can return a function
def cat(s1: String)(s2: String) = s1 + s2
def cat(s1: String) = (s2: String) => s1 + s2
cat("foo")("bar") > java.lang.String = foobar
cat("foo") returns a function {(s2 : Int) => “foo” + s2}, to which is passed an argument "bar"
Both notations are correct
Curryingdef cat(s1: String, s2: String) = s1 + s2> cat: (String,String) java.lang.String
val curryCat = Function.curried(cat _)> curryCat: (String) => (String) => java.lang.String = <function>
cat("foo", "bar") == curryCat("foo")("bar")> res2: Boolean = true
Function.curried() converts functions to their curried form
Partial function application
val curryCat = Function.curried(cat _)> curryCat: (String) => (String) => java.lang.String = <function>
val partialCurryCat = curryCat("foo")(_)> partialCurryCat: (String) => java.lang.String = <function>
partialCurryCat("bar")> res3: java.lang.String = foobar
Partial application= we didn’t passed all parameters
Partial function application
val numbers= List(1, 2, 3, 4, 5);
println( numbers map ( 8 +))
def plus(a : Int)(b : Int) : Int = a + bval plusFive = plus(5) _println( numbers map plusFive )
f(B : b) : C
f(A : a)(B : b) : C
It is not needed to pass all parametersUses Currying
„There are two ways of constructing a software design. One way is to make it so simple that there are obviously no deficiencies. And the other way is to make it so complicated that there are no obvious deficiencies.”
C.A.R. Hoare, author of Quicksort
Quicksortdef sort(xs: Array[Int]) {def swap(i: Int, j: Int) {
val t = xs(i); xs(i) = xs(j); xs(j) = t}def sort1(l: Int, r: Int) {
val pivot = xs((l + r) / 2)var i = l; var j = rwhile (i <= j) {
while (xs(i) < pivot) i += 1while (xs(j) > pivot) j -= 1if (i <= j) {swap(i, j)i += 1j -= 1}
}if (l < j) sort1(l, j)if (j < r) sort1(i, r)
}sort1(0, xs.length 1)
}
def sort(xs: Array[Int]): Array[Int] =if (xs.length <= 1) xselse {
val pivot = xs(xs.length / 2)Array.concat( sort(xs filter (pivot >)), xs filter (pivot ==), sort(xs filter (pivot <)))
}
Functional
Imperative
Implicits
Implicit conversions
Implicits
implicit def intToPimpedInt(i : Int) = new PimpedInt(i)
def main(args: Array[String]) {
3 times println("sdfs")
println(5 minutes)}
class PimpedInt(i : Int){def times(f : => Unit) : Unit = for(x <- 0 until i) fdef seconds = 1000 * idef minutes = 60 * seconds
}
intToPimpedInt(3).times(println("sdfs"))
times(f : => Unit)seconds()minutes()
println(intToPimpedInt(3).minutes())
Only single implicit can be used (they
can not chain)!
object Complex {val i = new Complex(0, 1)implicit def double2complex(x: Double) : Complex = new Complex(x, 0)
}class Complex(val re: Double, val im: Double) {
def + (that: Complex): Complex = new Complex(this.re + that.re, this.im + that.im)def - (that: Complex): Complex = new Complex(this.re - that.re, this.im - that.im)def * (that: Complex): Complex = new Complex(this.re * that.re - this.im * that.im,
this.re * that.im + this.im * that.re)def / (that: Complex): Complex = {
val denom = that.re * that.re + that.im * that.im new Complex((this.re * that.re + this.im * that.im) / denom, (this.im * that.re - this.re * that.im) / denom)
}override def toString = re+(if (im < 0) "-"+(-im) else "+"+im)+"*i"
}
By importing Complex one can use complex numbers like they were built in language feature.
import pads.Complex._object ComplexTest {
def main(args: Array[String]) {val x : Complex = 4 + 3 * ival y : Complex = x * i println(y)
}}
Structural typing
“Type safe duck typing”
Structural typingclass Employee {
var meno : String = null}
class Firm {var title : String = nulldef name = "Firm is called " + title
}
Attribute name
Method name
Employee and Firm does not have a common supertype
Structural typingdef getName(x : {val name : String}) : String = x.namedef setName(x : {var name : String}) : Unit = x.name = "new name"
val employee = new Employee()employee.name = “Kate"val firm = new Firm()firm.name = "PosAm"
println (getName(employee))println (getName(firm))
println (setName(employee))println (setName(firm)) //ERROR
type mismatch; found : firm.type (with underlying type pads.Firm) required: AnyRef{def name: String; def name_=(x$1: String): Unit}
DSL - domain specific language
ImplicitsHigher order functionsOptional dots, semi-colons, parenthesesOperators like methodsCurrying
DSL
External DSL+ own language, freedom- need to write a parser- extensible from inside
Internal DSL+ extensible from outside,
it’s just a library- syntax of a host language
Parser library in Scala simplifies writing external DSLs
Implicits, higher-order functions, optional dots and brackets, operator methods and currying simplifies writing of internal DSLs
External DSL written with the help of library scala.util.parsing.combinator._
/** @return Parser[Money] */ def percentage = toBe ~> doubleNumber <~ "percent" <~ "of" <~ "gross" ^^ { percentage => grossAmount * (percentage / 100.) }
def amount = toBe ~> doubleNumber <~ "in" <~ "gross" <~ "currency" ^^ { Money(_) }
def toBe = "is" | "are"
def doubleNumber = floatingPointNumber ^^ { _.toDouble }
|, ~> , <~, ^^ are just functionsLibrary pasing.combinator is internal DSL
^^
/** A parser combinator for function application * *<p>`p ^^ f' succeeds if `p' succeeds; it returns `f' applied to the result of `p'.</p> * * @param f a function that will be applied to this parser's result (see `map' in `ParseResult'). * @return a parser that has the same behaviour as the current parser, but whose result is * transformed by `f'. */ def ^^ [U](f: T => U): Parser[U] = map(f).named(toString+"^^")
Trait
Mixins Dependency injection Data, context & interaction
Types
Class Object TraitObject has only a single instance. One can obtain this instance by writing object’s name. It can be extended by the Traits.
Like an interface/abstract class.Can have an implementation.Can not have a constructor.
Class can inherit from one class but it can be extended by several Traits.
Dependency injectionclass UserRepository { def authenticate(user: User): User = { println("authenticating user: " + user) user } def create(user: User) = println("creating user: " + user) def delete(user: User) = println("deleting user: " + user) }
class UserService { def authenticate(username: String, password: String): User = userRepository.authenticate(username, password) def create(username: String, password: String) = userRepository.create(new User(username, password)) def delete(user: User) = All is statically typed. userRepository.delete(user) }
UserRepository can have more implementations.
UserService is dependent onUserRepository. It can have more implementations as well.
Cake Pattern
trait UserRepositoryComponent { val userRepository: UserRepository class UserRepository { ... } }
trait UserServiceComponent { this: UserRepositoryComponent =>
val userService: UserService
class UserService { ... }}
Classes are wrapped in components (traits), where its dependencies are declared.
Cake Pattern
object ComponentRegistry extends UserServiceComponent with UserRepositoryComponent { val userRepository = new UserRepository val userService = new UserService}
In the result object we set implementations of all necessary components. Every component uses right implementation on which it is dependent.
Case class
ADTPattern matching
Pattern matching
val randomInt = new Random().nextInt(10)
randomInt match { case 7 => println( "lucky seven!" ) case otherNumber => println(„not seven " + otherNumber)}
Matching by values
Java swicht-case analogy
Pattern matching
val sundries = List(23, "Hello", 8.5, 'q') for (sundry <- sundries) { sundry match { case i: Int => println("got an Integer: " + i) case s: String => println("got a String: " + s) case f: Double => println("got a Double: " + f) case other => println("got something else: " + other) } }
Matching by type
Pattern matching
val willWork = List(1, 3, 23, 90)val willNotWork = List(4, 18, 52)val empty = List()
for (l <- List(willWork, willNotWork, empty)) { l match { case List(_, 3, _, _) => println("4 elements, with the 2nd being '3'.") case List(_*) => println("Any other list with 0 or more elements.") }}
Matching of sequences
Pattern matching
val tupA = ("Good", "Morning!") val tupB = ("Guten", "Tag!") for (tup <- List(tupA, tupB)) { tup match { case (thingOne, thingTwo) if thingOne == "Good"
=> println("A two-tuple starting with 'Good'.") case (thingOne, thingTwo)
=> println("Two things: " + thingOne + " and " + thingTwo) } }
Matching of tuples
Pattern matching
case class Person(name: String, age: Int)val alice = new Person("Alice", 25)val bob = new Person("Bob", 32)val charlie = new Person("Charlie", 32)
for (person <- List(alice, bob, charlie)) { person match { case Person("Alice", 25) => println("Hi Alice!") case Person("Bob", 32) => println("Hi Bob!") case Person(name, age) => println(age + " years old person named " + name ) }}
Matching of case classes
Recap
Scala is a multi-paradigm languageObject-oriented – objects, classes, traits,
inheritance, polymorphism, encapsulationFunctional – function is a value, pattern matching,
parametric polymorphism (generics), case classes(Dynamic-types) – implicits, traits, structural
typing, type inference These features give programmers a feel of
language with dynamic types. Although all types are checked in compilation time.
And I haven’t mention
• Implicit function parameters• Lazy values• XML built in syntax• Actor
...
And it works with Hibernate, Spring, Guice, Maven, Wicket...
Finally end