Upload
buianh
View
224
Download
0
Embed Size (px)
Citation preview
Três anos de Scala no NewsMonitor
Felipe Hummel
• Site profissional para monitoramento de notícias em tempo real
• 170M de notícias
• 5M/mês
• 2 Devs Backend + 3 Devs PHP/Frontend
• ~30K linhas de código Scala
Como escolhemos Scala• busk.com
• Fechado e pivotado para o NewsMonitor
• Decisão de reaproveitar código legado em Ruby ou criar novo
• Aplicação ficou em PHP
• Backend em Scala
Elastic SearchElastic Search
Crawlers
Arquitetura Geral
Stark Indexer
MySQLMySQL Redis
Search API
Elastic Search
autocomplete
Arquitetura Coleta de Notícias
SocialCrawler
Feed Crawler
Article Crawler
Site Crawler
Fila de ColetaArticle
Crawler
Article Crawler
…Article
Crawler
SeedCrawlerSeedCrawlerSeedCrawler
Scala
Scala é muito boa como dizem
Scala, por quê?• Concisão de código sem perca de legibilidade
• Inferência de tipos
• Sintaxe extremamente sucinta para funções anônimas
• Coleções: List, Array, Map, Seq, Set, String
• map, filter, groupBy, sortBy, distinct
• Quase tudo faz parte da biblioteca e não é sintaxe especial da linguagem
Scala, por quê?
val idsDeAlunos = List(1423, 23245, 5343) val cursosPorNome = Map("Computação" -> 1234, "Matemática" -> 423, "Física" -> 5322, "Biologia" -> 1312)
Scala, por quê?
val idsDeAlunos = List(1423, 23245, 5343) val cursosPorNome = Map("Computação" -> 1234, "Matemática" -> 423, "Física" -> 5322, "Biologia" -> 1312)
Inferência de tipos
Scala, por quê?
val idsDeAlunos = List(1423, 23245, 5343) val cursosPorNome = Map("Computação" -> 1234, "Matemática" -> 423, "Física" -> 5322, "Biologia" -> 1312)
Inferência de tipos
Scala, por quê?
val idsDeAlunos = List(1423, 23245, 5343) val cursosPorNome = Map("Computação" -> 1234, "Matemática" -> 423, "Física" -> 5322, "Biologia" -> 1312)
Não é sintaxe especial
Scala, por quê?
val idsDeAlunos = List(1423, 23245, 5343) val cursosPorNome = Map("Computação" -> 1234, "Matemática" -> 423, "Física" -> 5322, "Biologia" -> 1312)
Scala, por quê?
case class Aluno(id: Int, curso: Curso, idade: Int) val alunosPorIdade = idsDeAlunos.map( id => carregaAluno(id) )
Scala, por quê?
case class Aluno(id: Int, curso: Curso, idade: Int) val alunosPorIdade = idsDeAlunos.map( id => carregaAluno(id) ) .filter(_.curso.nome == "Computação")
Scala, por quê?
case class Aluno(id: Int, curso: Curso, idade: Int) val alunosPorIdade = idsDeAlunos.map( id => carregaAluno(id) ) .filter(_.curso.nome == "Computação") .filter(_.idade > 18)
Scala, por quê?
case class Aluno(id: Int, curso: Curso, idade: Int) val alunosPorIdade = idsDeAlunos.map( id => carregaAluno(id) ) .filter(_.curso.nome == "Computação") .filter(_.idade > 18) .groupBy(_.idade)
Scala, por quê?
case class Aluno(id: Int, curso: Curso, idade: Int) val alunosPorIdade: Map[Int, List[Aluno]] = idsDeAlunos.map( id => carregaAluno(id) ) .filter(_.curso.nome == "Computação") .filter(_.idade > 18) .groupBy(_.idade)
Scala, por quê?
case class Aluno(id: Int, curso: Curso, idade: Int) val alunosPorIdade: Map[Int, List[Aluno]] = idsDeAlunos.map( id => carregaAluno(id) ) .filter(_.curso.nome == "Computação") .filter(_.idade > 18) .groupBy(_.idade)
funções anônimas sucintas
Scala, por quê?
• Várias pequenas funcionalidades que ajudam muito no dia a dia
• Default e named parameters
• case classes: syntactic sugar que vale a pena
• Pattern Matching: switch case+++
• Preferência por imutabilidade mas sempre dando opção por mutabilidade (quando necessária)
Scala é isso
80% de Scala é isso
Scala pode ser complicada como dizem
__ _ _ _ Placeholder __ __ _ _
val lista = List(“maria”, “José”, “joão”, “Carlos") lista.map( n => n.toUpperCase ) // List(“MARIA”, …) lista.map( _.toUpperCase ) // List(“MARIA”, …)
__ _ _ _ Placeholder __ __ _ _
val lista = List(“maria”, “José”, “joão”, “Carlos") lista.map( n => n.toUpperCase ) // List(“MARIA”, …) lista.map( _.toUpperCase ) // List(“MARIA”, …) lista.map( _.map(_.toUpper)) // List(“MARIA”, …)
__ _ _ _ Placeholder __ __ _ _
val lista = List(“maria”, “José”, “joão”, “Carlos") lista.map( n => n.toUpperCase ) // List(“MARIA”, …) lista.map( _.toUpperCase ) // List(“MARIA”, …) lista.map( _.map(_.toUpper)) // List(“MARIA”, …) lista.map { str => str.map( c => c.toUpper) } // List(“MARIA”, …)
Implicits conversions
10.seconds // Int não tem o método seconds ^~~~~~ erro de compilação
Implicits conversions
import scala.concurrent.duration.DurationInt 10.seconds // Int agora “tem" o método seconds
Implicits conversions import scala.concurrent.duration.DurationInt val time: Duration = 10.seconds
import org.scalatest.Matchers._
val resultado: String = … resultado should equal ("42")
Implicits conversionsimport wildcard
implicit conversions
• Conceito similar ao que dá pra fazer em Ruby ou Javascript, estendendo classes mas com tipagem estática!
• Você precisa importar explicitamente a conversão
• Cuidado para não abusar
• Melhor usado em DSLs
Interoperabilidade com Java• Em geral funciona bem
• Mas alguns conceitos de Java e Scala são irreconciliáveis
• Na prática as 3 maiores dores são:
• Código Java que acha legal retornar null
• Código Scala que usa coleções Java e vice-versa
• Tipos primitivos: Integer e int (Java) <-> Int (Scala)
Scala é complicada como dizem
Scala é complicada como dizem
em poucas coisas
Compilador• Poderoso mas “lento”
• Pra quem vem de Java, Go e linguagens dinâmicas
• Rápido pra quem vem de C++ • Na prática: sbt ~compile ou sbt ~test
Estilos de Scala• Muitas formas de escrever o mesmo código
• Um mais cool, outro mais funcional, outro mais imperativo
• Qual usar?
Estilos de Scala
val lista = List(0, 1, 2, 3, 4) lista.map( n => n + 1 )
Estilos de Scala
val lista = List(0, 1, 2, 3, 4) lista.map { n => n + 1 }
Estilos de Scala
val lista = List(0, 1, 2, 3, 4) lista map { n => n + 1 }
Estilos de Scala
val lista = List(0, 1, 2, 3, 4) lista.map { _ + 1 }
Estilos de Scala
val lista = List(0, 1, 2, 3, 4) lista.map ( _ + 1 )
Estilos de Scala
val lista = List(0, 1, 2, 3, 4) def add1(n: Int) = n + 1 lista.map (add1)
Estilos de Scala
val lista = List(0, 1, 2, 3, 4) def add1(n: Int) = n + 1 lista map add1
Estilos de Scala
lista.map ( n => n + 1 ) <—- Usamos lista.map { n => n + 1 } <—- Usamos lista map { n => n + 1 } lista.map { _ + 1 } lista.map ( _ + 1 ) <—- Usamos lista.map (add1) <—- Usamos lista map add1
Uso de memória e tempo de startup
• Estar na JVM tem vantagens e desvantagens
• Uso de memória
• Scala piora um pouquinho a situação
• Queremos structs! Value Types (Java 9?)
• Go e Rust podem ser alternativas em casos que isso é crítico
Scala Avançado e Curva de Aprendizado
• Algumas partes da comunidade enveredam demais pelo caminho funcional
• Você não é obrigado a usar. Mas a divisão da comunidade não é legal
• Com grandes poderes vem grandes responsabilidades
• Abuso de features (overloading de operadores)
• <o> <!> <<! ># >! >:> >|> (NÃAO, só DSLs onde operador já faz sentido)
• Aprendizado
• Muito fácil fazer Scala “javado” (mas é um início fácil)
• Traz conceitos novos e não familiares para quem vem da linhagem de C: implicits, pattern matching, typeclasses
Scala mudou a forma
como programamos
Parar de acessar o que não está lá
• NullPointerException (Java)
• undefined is not a function (Javascript)
• AttributeError: 'NoneType' object has no attribute (Python)
• Call to a member function on a non-object (PHP)
Parar de acessar o que não está lá
• NullPointerException (Java)
• undefined is not a function (Javascript)
• AttributeError: 'NoneType' object has no attribute (Python)
• Call to a member function on a non-object (PHP)
Null Pointer Exc… NÃO
• NullPointerException (Java)
• undefined is not a function (Javascript)
• AttributeError: 'NoneType' object has no attribute (Python)
• Call to a member function on a non-object (PHP)
SCALA
NÃO TEM
!!!!!!!!!
Null Pointer Exc… NÃO
• Uso de Option[MeuTipo] quando necessário
Tipagem Estática!• Linguagens com tipagem estáticas pegaram fama de serem
verbosas
• Não necessariamente
• Scala consegue ser tão concisa quanto as linguagens dinâmicas
• Tendência de adicionar tipos opcionais nas linguagens dinâmicas
• Javascript, PHP, Python
• Difícil viver sem um compilador me ajudando
Imutabilidade• Coisas a menos pra guardar na sua cabeça
• Não me preocupo se alguém pode ou vai mudar meu objeto
• Você pode passar objetos imutáveis pra lá e pra cá de boa
• Thread-safe por padrão
• Meio que obrigatório para chaves de Map, valores dentro de Set ou Caches em geral
• Se você observa um valor numa variável, você sabe que ela foi sempre aquilo
public class Person { private final String firstName; private final String lastName;
String getFirstName() { return firstName; } String getLastName() { return lastName; } public Person(String first, String last) { this.firstName = first; this.lastName = last; } public int hashCode() { return goodHashCode(firstName, lastName); } public boolean equals(Object o) { if ( this == aThat ) return true; if ( !(aThat instanceof Person) ) return false; Person that = (Person)aThat; return EqualsUtil.areEqual(this.firstName, that.firstName) & EqualsUtil.areEqual(this.lastName, that.lastName); } }
case class Person(firstName: String, lastName: String)
imutável é a palavra
Scala é a melhor coisa do mundo?
Scala é a melhor coisa do mundo?
$escapedName = mysql_escape_string($u) $escapedCurso = mysql_real_escape_string($c) $sql = “SELECT * FROM usuarios WHERE name LIKE '%”.$escapedName."%’ AND curso = ‘“.$escapedCurso;
A minha linguagem/framework/biblioteca
não é a melhor possível
Mais conciso e expressivo que Scala é impossível
Mais conciso e expressivo que Haskell é impossível
Programação é sempre um comando seguido do outro
Programação pode ser só (funções (chamando (funções ‘!’)))
em Clojure
Programar interfaces é ter que lidar com estado mutável o tempo todo
Programar interfaces é isolar o estado mutável com ReactJS
Ou damos free() na mão ou usamos Garbage Collector
Ou damos free() na mão ou usamos Garbage Collector
ou usamos Rust e o seu sistema de ownership
Sempre dá pra melhorar
Akka
Akka :)• Simplifica muito o modelo mental para trabalhar com concorrência
• Cria "bolhas" onde você pode ser mutável à vontade (com moderação)
• Muito bom quando você tem Actors de vida longa e com estado mutável
• Não precisa lidar com criação/manutenção de filas (na maior parte do tempo)
• Tunar threadpools (dispatchers) não é necessário no início e é bem fácil (via config)
• O código do Actor não contém (quase) nada de lógica lidando com concorrência, threads ou threadpool
Akka :(• Toma conta do código (framework)
• A relação entre actors não é tão óbvia no código quanto a relação entre objetos num código tradicional (perca de tipagem)
• Você pode acabar tunando ExecutionContexts e ThreadPool de qualquer forma. E não é trivial (independente do Akka).
• Mais "difícil" de testar
• Dá pra isolar algumas coisas e criar determinismo
• No final das contas tudo é concorrente
• Debugar é bem diferente
• Rastrear de onde uma coisa veio, pra onde vai…