Scala
Arthur Felipe
Bruno Pimentel
Carlos Junior
Mateus Araujo
Roteiro
•
•
•
•
•
Introdução e História da Linguagem
Sintaxe
Aplicações e futuro
Conclusão
Referências
Introdução e História da Linguagem
• O nome Scala significa “linguagem
escalável”
• Projetada para integrar linguagem
orientada a objetos e programação
funcional
• Executado em plataforma Java
• Mesmo modelo de compilação como Java
e C#
Introdução e História da Linguagem
• Recursos orientados a objetos
Cada valor é um objeto, onde seus
comportamentos são descritos por classes
o Abstrações de classes
o
• Programação Funcional
o
o
o
o
Definição de funções
Funções de ordem superior
Casamento de padrões
Tipos algébricos
Introdução e História da Linguagem
• O design começou em 2001 na École
Polytechnique Fédérale de
Lausanne (EPFL) por Martin Odersky
• Foi liberado no fim de 2003 e início de
2004 na plataforma Java
• Em Junho de 2004 na plataforma .NET
Introdução e História da Linguagem
• Em 2009, Twitter mudou grande boa parte
dos seus back-end do Ruby para Scala e
pretende mudar o resto
• A Wattzon afirmou que toda a sua
plataforma foi escrita em Scala
Por quê utilizar Scala?
Scala é Escalável
• Adequada para tarefas de qualquer
tamanho
o Muito pequenas (scripts)
o Muito grandes (frameworks)
Scala é Multiplataforma
• Roda em vários ambientes
o Java Virtual Machine (JVM)
o Plataforma .NET
o Java Platform, Micro Edition (Java ME)
Scala é Elegante
• Linguagem simples que une conceitos de
OO e funcional
o Facilita a manutenção dos programas
o Usa bibliotecas ao invés de outras
linguagens para desenvolver módulos
específicos
o Simplicidade de funcional + poder de
Objetos
Sintaxe
Variáveis e Valores
> var a = "Hello "
a: java.lang.String = Hello
> a = a + "World!"
a: java.lang.String = Hello World!
> println(a)
Hello World!
Variáveis e Valores
> val b = "Bob Esponja"
b: java.lang.String = Bob Esponja
> b += " e Patrick"
error: reassignment to val
b += " e Patrick"
^
Inferência de tipos
> val t = 1 / 2
t: Int = 0
> val d: Double = 1 / 2
f: Double = 0.0
> val g : Double = 1.0 / 2.0
g: Double = 0.5
Funções
> def scale = 5 //Uma função constante
scale: Int
> def square(x: Double) = x * x
square: (Double)Double
> square(2)
unnamed0: Double = 4.0
> def sumOfSquares(x: Double, y: Double) = square(x) +
> square(y)
sumOfSquares: (Double,Double)Double
Funções
> def min (x:Int, y:Int) = {
>
if (x < y) x
>
else y
> }
min: (Int,Int)Int
> def invert (x: Int) = -x
invert: (Int)Int
> def invert (x: Int) : Int = { return -x }
invert: (Int)Int
Tudo é um objeto
Não existem primitivas.
> 1233.hashCode
res0: Int = 1233
Toda operação é uma chamada de uma função:
> 2 + 2 == 2.+(2)
res4: Boolean = true
> Console.println("Ronaldo!")
Ronaldo!
> Console println "Brilha muito no Corinthians"
...
Estruturas de controle
Praticamente todas as estruturas de controle são também
expressões:
> val x = if (0 < 1) 10 else 20
x: Int = 10
> val y = if (0 < 1) 10 //sem o else
y: Unit = () //Unit é o 'Null' em Scala
Estruturas de controle
Compreensões utilizando 'for'
> val places = List("Olinda", "Recife", "São Paulo", "Manaus")
> for (place <- places)
> println(place)
Olinda \n Recife \n São Paulo \n Manaus \n
> val places2 =
List("Olinda", "Recife", "São Paulo", "Manaus","São Vicente")
> for (place <- places2 if place.contains("São"))
> println(place)
São Paulo \n São Vicente \n
Estruturas de controle
Utilizando `yield` para passagem de valores:
val dogBreeds = List("Doberman", "Yorkshire Terrier",
"Dachshund", "Scottish Terrier", "Great Dane",
"Portuguese Water Dog")
val filteredBreeds = for {
breed <- dogBreeds
if breed.contains("Terrier")
if !breed.startsWith("Yorkshire")
} yield breed
Estruturas de controle
Utilizando ranges.
> for (i <-1 to 10) println(i)
1 \n 2 \n ...
Utilizando `while`
> var count = 0
> while (count < 10) {
> count += 1
> println(count)
>}
Funções como objetos
Somatório:
> def sum(f: Int => Int, a: Int, b: Int): Int =
> if (a > b) 0 else f(a) + sum(f, a + 1, b)
sum: ((Int) => Int,Int,Int)Int
Utilizando uma função anônima:
> def sumSquares(a:Int, b:Int):Int = sum(((x:Int) => x*x),a,b)
sumSquares: (Int,Int)Int
> sumSquares(1,10)
res1: Int = 385
Funções como objetos
Currying:
> def filter(xs: List[Int], p: Int => Boolean): List[Int] =
> if (xs.isEmpty) xs
> else if (p(xs.head)) xs.head :: filter(xs.tail, p)
> else filter(xs.tail, p)
filter: (List[Int],(Int) => Boolean)List[Int]
> def modN(n: Int)(x: Int) = ((x % n) == 0)
modN: (Int)(Int)Boolean
> filter(List(1,2,3,4,5,6,7,8,9,0),modN(2))
List[Int] = List(2, 4, 6, 8, 0)
Exemplo: Quicksort (funcional)
def sort(xs: Array[Int]): Array[Int] = {
if (xs.length <= 1) xs
else {
val pivot = xs(xs.length / 2)
Array.concat(
sort(xs filter (pivot >)),
xs filter (pivot ==),
sort(xs filter (pivot <)))
}
}
Para testar:
> sort(Array(15,3,44,457,18))
Exemplo: Quicksort (imperativo)
def sort(xs: Array[Int]) {
def swap(i: Int, j: Int) {
val t = xs(i); xs(i) = xs(j); xs(j) = t
}
def particionar(l: Int, r: Int) {...}
particionar(0, xs.length - 1)
}
Para testar:
> sort(Array(15,3,44,457,18))
Exemplo: Quicksort (imperativo)
def particionar(l: Int, r: Int) {
val pivot = xs((l + r) / 2)
var i = l; var j = r
while (i <= j) {
while (xs(i) < pivot) i += 1
while (xs(j) > pivot) j -= 1
if (i <= j) {
swap(i, j)
i += 1
j -= 1
}
}
if (l < j) particionar(l, j)
if (j < r) particionar(i, r)
}
Pattern Matching
> def matchTest(x: Int): String = x match {
> case 1 => "one"
> case 2 => "two"
> case _ => "many"
> }
matchTest: (Int)String
> println(matchTest(3))
many
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("Four elements, with
the > 2nd being '3'.")
> case List(_*) => println("Any other list with 0 or more
> elements.")
> }
>}
Listas
> val l1 = List(1, 2, 3)
> val l2: List[Int] = 4 :: 5 :: 6 :: Nil
> val l3 = l1 ::: l2
l1: List[Int] = List(1, 2, 3)
l2: List[Int] = List(4, 5, 6)
l3: List[Int] = List(1, 2, 3, 4, 5, 6)
> l3.foreach (n => println(n))
1 \n 2 \n ...
Listas
Alguns métodos para se trabalhar com listas:
val lista = "Will" :: "fill" :: "until" :: Nil
lista(2) //retorna o segundo elemento da lista
lista.count(s => s.length == 4)
lista.drop(2) //lista sem os 2 primeiros elementos
lista.exists(s => s == "until")
lista.filter(s => s.length == 4)
lista.map(s => s + "y")
lista.isEmpty
Tuplas
> def tupleator(x1: Any, x2: Any, x3: Any) = (x1, x2, x3)
> val t = tupleator("Hello", 1, 2.3)
> println( "Print the whole tuple: " + t )
> println( "Print the first item: " + t._1 )
> println( "Print the second item: " + t._2 )
> println( "Print the third item: " + t._3 )
> val (t1, t2, t3) = tupleator("World", '!', 0x22)
> println( t1 + " " + t2 + " " + t3 )
Mapas
> val stateCapitals = Map("PE" -> "Recife", "AL" -> "Maceió")
stateCapitals: scala.collection.immutable (...)
> println("Imprime os objetos dentro de Options")
> println( "PE: " + stateCapitals.get("PE") )
> println( "SP: " + stateCapitals.get("SP") )
"Imprime os objetos dentro de Options"
PE: Some(Recife)
SP: None
Mapas
> println( "Retirando o objeto da Option...")
> println( "PE: " + stateCapitals.get("PE").get )
> println( "SP: " +
> stateCapitals.get("SP").getOrElse("Oops2!") )
Retirando o objeto da Option...
PE: Recife
SP: Oops2!
Arrays
> val names = Array("José","Maria")
> names(0) = "João"
> val cities = new Array[String](2)
> cities(0) = "Recife"
> cities(1) = "Olinda"
> cities.foreach{println}
Classes e Objetos
• Em Scala todo valor é um objeto.
• Tipos e comportamentos de objetos são descritos por
classes e traits.
• Todos os valores são instâncias de classes.
Hierarquia de Classes
Classes e Objetos
Classes são templates estáticos que podem ser
instanciados em vários objetos durante o tempo de
execução.
class Point(xc: Int, yc: Int) {
var x: Int = xc
var y: Int = yc
def move(dx: Int, dy: Int) {
x = x + dx
y = y + dy
}
override def toString(): String = "(" + x + ", " + y + ")";
}
Classes e Objetos
Object: classe com uma única instância(singleton).
Classes são instanciadas com a primitiva new.
object Classes {
def main(args: Array[String]) {
val pt = new Point(1, 2)
val pt2 = new Point(3,4)
println(pt, pt2)
}
}
Classes e Objetos
• Classes podem extender outras classes.
• Métodos podem ser sobrescritos.
class ColorPoint(u: Int, v: Int, c: String) extends Point(u, v) {
val color: String = c
def compareWith(pt: ColorPoint): Boolean =
(pt.x == x) && (pt.y == y) && (pt.color == color)
override def move(dx: Int, dy: Int): ColorPoint =
new ColorPoint(x + dy, y + dy, color)
}
Classes e Objetos
Adicionando algumas restrições em classes:
class Person(name: String, age: Int) {
if (age < 0) throw new IllegalArgumentException
def sayHello() { println("Hello, " + name) }
}
class Adult(name: String, age: Int) {
require (age >= 18)
def sayHello() { println("Já pode tomar cana, " + name) }
}
Case Classes
São classes que exportam seus parâmetros de construtor.
Fornecem um mecanismo de decomposição recursiva
através de pattern matching.
abstract class Term
case class Var(name: String) extends Term
case class Fun(arg: String, body: Term) extends Term
case class App(f: Term, v: Term) extends Term
Case Classes
object TermTest extends Application {
def printTerm(term: Term) {
term match {
case Var(n) =>
print(n)
case Fun(x, b) =>
print("^" + x + ".")
printTerm(b)
case App(f, v) =>
Console.print("(")
printTerm(f)
print(" ")
printTerm(v)
print(")")
}
}
Traits
Similares a interface em java.
Permitem implementação parcial.
trait Similaridade {
def eSimilar(x:Any) : Boolean
def naoSimilar(x:Any) : Boolean = !eSimilar(x)
}
Traits
O metodo "naoSimilar" não precisa ser implementado pelas
classes que extenderem o trait Similaridade.
Exemplo:
class Ponto(xc: Int, yc: Int) extends Similaridade {
var x: Int = xc
var y: Int = yc
def eSimilar(obj: Any) =
obj.isInstanceOf[Ponto] &&
obj.asInstanceOf[Ponto].x == x
}
Composição Mixin
Especie de herança multipla.
Herda os métodos da superclasse + os novos métodos das
classes mixins.
Mixin: classe que aparece associada á palavra with na
declaração de classes.
Apenas trait pode ser usada como mixin.
Composição Mixin
abstract class AbsIterator {
type T
def hasNext: Boolean
def next: T
}
trait RichIterator extends AbsIterator {
def foreach(f: T => Unit) { while (hasNext) f(next) }
}
class StringIterator(s: String) extends AbsIterator {
type T = Char
private var i = 0
def hasNext = i < s.length()
def next = { val ch = s charAt i; i += 1; ch }
}
object StringIteratorTest {
def main(args: Array[String]) {
class Iter extends StringIterator(args(0)) with RichIterator
val iter = new Iter
iter foreach println
}
}
Generics
class Pilha[T] {
var elems: List[T] = Nil
def push(x: T) { elems = x :: elems }
def top: T = elems.head
def pop() { elems = elems.tail }
}
...
val pilha = new Pilha[Int]
...
Estendendo a linguagem
Escalabilidade -> Extensibilidade
Exemplo: Tipos numéricos
• Linguagens atuais suportam int, float, double, long...
• Deveriam suportar também BigInt, BigDecimal,
Complex... ?
• Existem boas razões para suportar todos eles, mas
tornaria a linguagem muito complexa!
Solução: Permitir que os usuários extendam a linguagem
conforme suas necessidades!
Definindo um novo tipo de dados
class Rational(n: Int, d: Int) {
private def gcd(x: Int, y: Int): Int = {...}
private val g = gcd(n, d)
val numer: Int = n/g
val denom: Int = d/g
def + (that: Rational) = new Rational(numer * that.denom +
that.numer * denom, denom * that.denom)
def - (that: Rational) = new Rational(numer * that.denom that.numer * denom, denom * that.denom)
...
}
Definindo novas estruturas de controle
object TargetTest1 extends Application {
def whileLoop(cond: => Boolean)(body: => Unit): Unit =
if (cond) {
body
whileLoop(cond)(body)
}
var i = 10
whileLoop (i > 0) {
println(i)
i -= 1
}
}
Definindo novas estruturas de controle
Utilizando using para controle de recursos:
using (new BufferedReader(new FileReader(path))) {
f => println(f.readLine())
}
Ao invés de:
val f = new BufferedReader(new FileReader(path))
try {
println(f.readLine())
} finnaly {
if (f != null) f.close()
}
Definindo novas estruturas de controle
Implementação:
def using[T <: {def close() } ]
(resource: T)
(block: T => Unit) {
try {
block(resource)
} finally {
if (resource != null) resource.close()
}
}
Concorrência
Concorrência
• Gerenciar concorrência não é tarefa fácil em muitas
linguagens
o Como lidar com acesso simultâneo a memória, disco,
variáveis e filas de mensagens?
o Como saber o comportamento do programa com
5,10,500 processos simultâneos?
o Como gerenciar locks e notifiers?
Concorrência
Infelizmente gerenciar concorrência em outras linguagens
traz mais perguntas do que repostas.
Felizmente, Scala oferece uma abordagem elegante e
flexível para estes problemas.... Actors
Concorrência
Actors na teoria:
• São objetos que recebem mensagens e tomam ações
baseados nelas.
• Um Actor pode enviar a mensagem para outro ou criar
um novo Actor de acordo com a mensagem recebida.
• Não possuem sequência ou ordem de ações, não
compartilham estado global.
Concorrência
Actors na prática:
• Objeto que herda de scala.actors.Actor
• Principais métodos:
Concorrência
Actors na prática:
import scala.actors.Actor
class Redford extends Actor {
def act() {
println("A lot of what acting is, is paying attention.")
}
}
val robert = new Redford
robert.start
Concorrência
Actors na prática:
import scala.actors.Actor
import scala.actors.Actor._
val paulNewman = actor {
println("To be an actor, you have to be a child.")
}
Concorrência
Actors na prática: Enviando mensagem
import scala.actors.Actor
import scala.actors.Actor._
val fussyActor = actor {
loop {
receive {
case s: String => println("I got a String: " + s)
case i: Int => println("I got an Int: " + i.toString)
case _ => println("I have no idea what I just got.")
}
}
}
fussyActor ! "hi there"
fussyActor ! 23
fussyActor ! 3.33
Concorrência
Actors na prática (MailBox):
import scala.actors.Actor
import scala.actors.Actor._
val countActor = actor {
loop {
react {
case "how many?" => {
println("I've got " + mailboxSize.toString + " messages in my mailbox.")
}
}
}
}
countActor ! 1
countActor ! 2
countActor ! 3
countActor ! "how many?"
countActor ! "how many?"
countActor ! 4
countActor ! "how many?"
Concorrência
Actors na prática:
• Aplicação exemplo Loja do barbeiro
dorminhoco.
Aplicações e futuro
• Scala se encaixa no futuro multiplataforma
e multilinguagem das linguagens de
programação
• Scala é uma linguagem fácil de aprender
para programadores Java-like.
• Scala oferece mecanismos flefíveis para
tratar concorrência e paralelismo.
Aplicações e futuro
• Principais aplicações:
o Kestrel
 Substituiu o Starling (servidor de filas utilizado pelo
Twitter)
 Agilizou o processo de entrega de mensagens no Twitter e
resolveu antigos problemas de infra do Starling
o Lift
 Framework para criação de sistemas Web baseado em
Scala. Parecido com JSF, JBoss Seam.
 Como é baseado em Scala resolve facilmente problemas
de Java e oferece um novo ponto de vista sobre
construção de sistemas Web.
Conclusão
• Eficiente tanto para linguagens orientados
a objetos quanto funcionais
• Independência de plataforma
• É escalável: atende à demanda dos
usuários
• Linguagem de propósito geral com forma
elegante, concisa e type-safe
Referências
• http://www.scala-lang.org/
• http://en.wikipedia.org/wiki/Scala_(programming_
language)
• http://pt.wikipedia.org/wiki/Scala_(linguagem_de
_programação)
• Tutorial Scala - http://www.scala-lang.org/docu/
files/ScalaTutorial.pdf
Dúvidas?
Arthur Felipe
Bruno Pimentel
Carlos Junior
Mateus Araujo
Download

def