Scala é uma linguagem que mistura características de linguagens funcionais com características de orientação a objetos. Scala tem tipagem estática, ou seja, o tipo/classe de uma expressão ou coisa é conhecido sem que o programa precise ser executado. Mas diferente de outras linguagens "estáticas" como C/C++ e Java, Scala tem um jeitão mais dinâmico e limpo. Isso se deve, principalmente, à inferência de tipos (já já explico).
Para acompanhar o código é só instalar Scala e rodar o REPL (só rodar "scala" na linha de comando). Em Unixes é só rodar apt-get install scala (ou ports ou brew ou...)
blablabla QUERO CÓDIGO!!
Começando do começo: variáveis
Todas as declarações de variáveis são precedidas por val ou var. Se você pretende alterar o valor da "variável" use var e ela será mutável, senão use val. Usar val por padrão é uma boa prática.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var texto = "meu texto favorito" // var => mutável | |
// PANNNNNN erro de compilação. | |
// Tentando atribuir um inteiro para uma variável do tipo String!! | |
texto = 10 | |
// Não preciso dizer que meuInteiro é do tipo Int | |
var meuInteiro = funcaoComplexaQueRetornaUmNúmero(texto) | |
meuInteiro = 20 // ... de boa ... | |
meuInteiro = texto // PANNNNNN erro de compilação! |
Apesar de não ter deixado explícito os tipos das variáveis (Int e String) o compilador sabe que val numero é do tipo Int e que val texto é do tipo String. Isso é possível por que o compilador de Scala usa um algoritmo que infere qual o tipo daquela expressão. Isso é o que chamamos de inferência de tipos (ou type inference).
Pra quem vem de Java val e var são análogos a colocar ou não final antes de uma variável. Em C/C++ é análogo (mas não igual) ao const antes de declarações. Em PHP, Python e outras linguagens não temos um conceito similar, apesar de existirem formas de declarar constantes no código (Javascript aparentemente não tem).
O fato de Scala forçar você escolher, o tempo todo, entre val e var faz com que você rapidamente perceba que você não precisa de mutabilidade na maioria das vezes. Você acaba usando val pra quase tudo. Simplificando bastante na hora de entender qual valor determinada variável contém. Simples: sempre o valor atribuído na sua declaração.
O uso de val se alinha com o discurso de tentar usar sempre final e const que vemos na
literatura de Java e C++ (em livros como Effective Java e Effective C++).
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var texto = "meu texto favorito" // var => mutável | |
// PANNNNNN erro de compilação. | |
// Tentando atribuir um inteiro para uma variável do tipo String!! | |
texto = 10 | |
// Não preciso dizer que meuInteiro é do tipo Int | |
var meuInteiro = funcaoComplexaQueRetornaUmNúmero(texto) | |
meuInteiro = 20 // ... de boa ... | |
meuInteiro = texto // PANNNNNN erro de compilação! |
Funções
Mesmo com a inferência de tipos, uma hora você tem que dizer o tipo de alguma coisa, não tem como inferir o tipo de tudo (Haskell discorda). Um dos momentos de dizer os tipos é nos parâmetros de funções:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
def ola(nome: String): String = { | |
return "Olá " + nome + "!" | |
} | |
// mesma coisa que: | |
def ola(nome: String): String = { | |
// Scala assume que a última linha de uma função é o retorno | |
"Olá " + nome + "!" | |
} | |
// mesma coisa que: | |
// compilador infere que a função retorna String | |
def ola(nome: String) = { | |
"Olá " + nome + "!" | |
} | |
// mesma coisa que: | |
// forma mais compacta e usando interpolação de strings | |
// para usar interpolação é só colocar 's' e usar o $ | |
def ola(nome: String) = s"Olá $nome!" |
Coleções
Scala tem uma biblioteca de coleções (ou collections) bem completa e poderosa. A sintaxe para listas (List), dicionários (Map) e conjuntos (Set) é intuitiva e bem semelhante a linguagens dinâmicas.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val megasena = List(25, 38, 44, 46, 53, 54) // lista encadeada | |
for (numeroSorteado <- megasena) { | |
println(numero) | |
} | |
var i = 0 | |
while(i < megasena.size) { | |
println(megasena(i)) | |
i += 1 | |
} | |
if (megasena.size > 5) { | |
println("Ewww!") | |
} | |
else { | |
println("WAT!?") | |
} | |
val dicionario = Map("Amazonas" -> "Manaus", | |
"Paraná" -> "Curitiba", | |
"Goiás" -> "Goiânia") | |
val curitiba = dicionario("Paraná") | |
for ((estado, cidade) <- dicionario) { | |
println(s"$cidade é a capital do estado $estado") | |
} | |
// conjunto de elementos únicos (remove duplicados) | |
val cidades = Set("Manaus", "Curitiba", "Curitiba", "Rio de Janeiro") | |
println(cidades.contains("Curitiba")) // True | |
println(cidades("Curitiba")) // True | |
println(cidades.size) // 3 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
val nomes = List("José", "João", "Maria", "Fernanda") | |
val tamanhoNomes = nomes.map( pessoa => pessoa.size ) | |
println(tamanhoNomes) // List(4, 4, 5, 8) | |
def nomesMaioresQue(nomes: List[String], tamanho: Int) = | |
nomes.filter( nome => nome.size > tamanho) | |
nomesMaioresQue(nomes, 4) // List("Maria", "Fernanda") | |
nomesMaioresQue(nomes, 5) // List("Fernanda") | |
nomesMaioresQue(nomes, 3) // List("José", "João", "Maria", "Fernanda") | |
nomes.foreach ( nome => println(nome) ) |
No exemplo mais acima, quando criamos coleções passando o conteúdo (List(1, 2, 3)), não tivemos que dizer o tipo. O compilador, esperto como só ele, consegue inferir. Então, algo como List(1, 2, 3, 4) é exatamente o mesmo que List[Int](1, 2, 3, 4).
Classes
De cara a maior diferença das classes em Scala para as mesmas em Java, PHP ou C++ é que o construtor da classe é declarado junto do nome da própria classe. Ao invés de declarar uma função/método separado que age como construtor, o próprio corpo da classe já é seu construtor. Isso acaba economizando espaço.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Construtor da classe é (nome: String) | |
class MinhaClasse(nome: String) { | |
def olaQuem() = s"Olá $nome!" | |
} | |
val minha = new MinhaClasse("Felipe") | |
val msg = minha.olaQuem() | |
println(msg) // Olá Felipe! | |
class Pessoa(nome: String, idade: Int) { | |
def dizIdade() = println(s"$nome tem $idade anos") | |
} | |
val joao = new Pessoa("João", 30) | |
joao.nome // PANNNNN erro de compilação | |
// atributo nome não é acessível de fora da classe | |
// usar val ou var antes dos parâmetros do construtor | |
// os transformam em ATRIBUTOS acessíveis da classe | |
class BoaPessoa(val nome: String, var idade: Int) | |
val pedro = new BoaPessoa("Pedro", 50) | |
println(s"${pedro.nome} tem ${pedro.idade} anos") | |
pedro.idade = pedro.idade + 1 // ficou mais velho | |
println(pedro.idade) // 51 |
Mas o maior ganho é o fato de você poder declarar atributos diretamente nos parâmetros do construtor. Basta colocar val ou var antes de um parâmetro do construtor que ele vira um atributo da classe. Em Scala não existe a necessidade desse tipo de código:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// em Java: | |
class Ponto { | |
final int x; | |
final int y; | |
public Ponto(int x, int y) { | |
this.x = x; | |
this.y = y; | |
} | |
} | |
// ou em PHP: | |
class Ponto { | |
var $x; | |
var $y; | |
function __construct($x, $y) { | |
$this->x = $x; | |
$this->y = $y; | |
} | |
} |
Esse foi o primeiro post. Nos próximos vamos entrar em mais detalhes em cada um dos pontos falados aqui.
Ah! Quase esqueci de comentar. Pra quem não percebeu: ponto-e-vírgula é opcional em Scala! \o/