Programação, Scala

Aprenda Linguagem Scala – Parte I – Introdução

Nas próximas postagens, publicaremos uma série de artigos falando da linguagem Scala. Neste primeiro tutorial, introduziremos alguns conceitos básicos para começar a trabalhar com esta empolgante e inovadora linguagem.

Obtendo e Instalando Scala
A linguagem Scala funciona sobre a máquina virtual Java. Portanto, deve-se instalar uma JVM, de preferência a versão 1.7 mais atual.

A versão atual de Scala é 2.10*. Houveram grandes modificações da versão 2.9 para 2.10, logo recomendamos iniciar seus estudos pela versão mais atual.

* OBS: Este tutorial foi redigido quando 2.10.* era a última versão da linguagem Scala. Adequações para 2.11.* e futuras serão adicionadas conforme for possível. (Nota adicionada em Set/2014)

A forma mais rápida de iniciar os estudos de linguagem Scala é baixar o Scala IDE, ambiente baseado na plataforma Eclipse. Outra opção é utilizar o SBT, ou Simple Build Tool, o qual é capaz de baixar o compilador Scala como pré-requisito de um projeto SBT. Por fim, é possível baixar os binários da linguagem, os quais são apenas um conjunto de JARs que executarão sobre a JVM instalada.

Scala IDE
Em nossa série de tutoriais utilizaremos o Scala IDE, ambiente com suporte profissional oferecido pelos criadores da linguagem e idealizadores do sistema Typesafe, um ambiente completo para desenvolvimento de sistemas distribuídos em Scala.

Conceito da Linguagem
Scala tem como finalidade ser uma linguagem escalável: deve permitir a criação de scripts rápidos para tarefas simples, bem como a construção de grandes sistemas seguros e facilmente mantidos. Nas palavras de seus idealizadores, Scala deve “crescer juntamente com os requisitos do sistema”. Os criadores da linguagem buscaram adequar aquilo que percebiam como pontos negativos da linguagem Java, sem descartar a enorme base de código existente para aquela plataforma. Como resultado, programas Scala podem usar todas a bibliotecas e código legado de Java sem maiores incompatibilidades.

Hello World!
A forma mais simples de aprender Scala é utilizar o recurso Scala Worksheet. A Worksheet é um ambiente REPL(Read Eval Print Loop) onde comandos vão sendo interpretados em tempo real, e seu resultado vai sendo apresentado do lado direito do editor. Assim, é possível testar construções da linguagem com retorno de erros e sucessos em tempo real.

Programas da Worksheet não devem conter um método main, assim, este é o programa mais simples naquele ambiente:

object testando {
println("Olá, mundo!") //> Olá, mundo!
}

O comentário à direita foi inserido pelo Scala Worksheet, é a saída produzida pelo comando digitado à esquerda. A cada vez que o arquivo é salvo o código é avaliado novamente, e nova saída é produzida.

Scripts
Conforme mencionado anteriormente, Scala permite a criação de scripts semelhantes àqueles criados em outras linguagens dinâmicas como Perl e Ruby. O seguinte é um script completo em Scala:

println("Olá, mundo!")

Para executá-lo, basta chamar o interpretador Scala na linha de comando. Salve o arquivo e, em seguida digite:

scala meuscript.scala

(Para este teste será necessário haver instalado o ambiente Scala fora do IDE.)

Paradigma Objeto-Funcional
Scala é uma linguagem criada para ser elegante, permitindo idiomas e soluções no paradigma funcional, sem tornar obrigatório um determinado sistema de trabalho. Assim, Scala não impõe uma barreira aos desenvolvedores de outras linguagens, permitindo a programação imperativa como em C, orientada a objetos como em Java/C++ ou em estilo funcional como em ML ou Haskell. Scala é uma linguagem criada para ser produtiva, reconhecendo o fato de haverem incontáveis metodologias de trabalho e milhões de linhas de código de sistemas legados escritos em diversas linguagens distintas.

No entanto, Scala tem um conjunto de “idiomas” próprios desenvolvidos pela sua comunidade ao longo dos anos. Uma vasta biblioteca de estruturas de dados imutáveis foi adicionada à biblioteca padrão, fato que favorece a programação funcional. Praticamente todas as instruções da linguagem retornam algum valor, característica também oriunda das linguagens funcionais.

Tudo isso sem deixar de oferecer funcionalidades encontradas em linguagens tradicionalmente orientadas a objetos como hierarquia de tipos, tratamento de exceções, polimorfismo e padrões de uso conhecidos como singleton, facade, entre outros.

Assim, dizemos que Scala é uma linguagem que segue o paradigma Objeto-Funcional: favorece soluções que usam idiomas tipicamente encontrados em linguagens funcionais, porém permitindo(e encorajando) a programação orientada a objetos.

Um programa de exemplo
O seguinte trecho de código ilustra esta característica híbrida de linguagem:

object ScalaTutorialLiterati {
val alunosTurmaB : List[String] = List("Pedro","Joao","Lucia","Alessandra")
def main(args: Array[String]) = {
val ordenados = alunosTurmaB.sorted
println("Alunos da turma B: " + ordenados.mkString(", "))
}
}

Na linha 1, usamos a definição de objeto de Scala. Um object é uma classe estática que será automaticamente instanciada uma única vez ao executar o programa. É uma implementação do padrão singleton e de classes estáticas de Java. O método main é um velho conhecido dos programadores C, C++ e Java: é o ponto de entrada do programa, o qual recebe um único argumento, neste caso args, do tipo Array[String].

Em Scala, os [colchetes] são usados para definir o tipo de tipos parametrizados como fazemos em Java e C++ utilizando <>’s. E os tipos são escritos após o nome das variáveis, e separados destas por dois pontos:
val idade : Int = 19. Os vetores de Scala são indexados usando parênteses, como chamadas de métodos. No caso, o primeiro item de args poderia ser acessado assim:

println("Primeiro item de args: " + args(0) + "\n")

Note o uso de parenteses no lugar dos colchetes de Java.

Na linha 2 de nosso exemplo anterior definimos uma variável imutável usando a palavra-chave val. O nome da variável vem em seguida, com dois pontos e tipo (dois pontos e tipo são opcionais).

O método main é então definido, dentro do qual temos um valor imutável atribuído a ordenados. Note que ordenados tem seu tipo inferido e não possui dois pontos e o tipo explicitamente descrito. Scala “sabe” que a alunosTurmaB é uma lista de String, e o retorno desta lista, ordenada, é outra List, também de String.

Scala é fortemente tipada, e utiliza o paradigma de tipagem estática. Assim, os tipos devem ser de conhecimento do compilador antes da execução do programa. Esta característica oferece maior segurança, garantindo que tipos incompatíveis não serão errôneamente usados. Ao omitirmos o tipo no exemplo acima, o compilador inferiu o tipo e este foi compilado de acordo. Não houve a definição de uma variável de tipo genérico, como ocorre em Perl, por exemplo. A variável tem um tipo único, o qual é imutável.

Por fim, a chamada a println faz com que a String passada seja enviada à saída padrão, neste caso conectada ao console do Eclipse. Caso utilize a linha de comandos e um shell, a saída seria impressa em STDOUT.

A lista foi tratada de forma funcional, sem alterar os dados da List[String] original. A primeira lista foi ordenada pelo método sorted, e uma nova lista foi armazenada em ordenados. O valor a lista alunosTurmaB jamais foi modificado, esta é uma estrutura de dados imutável. O valor de ordenados é então convertido em uma cadeia de caracteres usando mkString com o separador(“, “). Novamente a lista ordenada não é modificada, seu valor é copiado e uma String é criada para acomodar seu retorno. A saída do programa é:

Alunos da turma B: Alessandra, Joao, Lucia, Pedro

Parenteses, colchetes e pontos
Em Scala os parenteses tem função semelhante a Java, com certas modificações. Em certos casos, os colchetes podem ser usados no lugar de parenteses, como no código a seguir:
for {aNome <- ordenados} { println("Aluno: " + aNome + "\n") }
for (aNome <- ordenados) ( println("Aluno: " + aNome + "\n") )

Os colchetes foram trocados por parênteses sem qualquer mudança no comportamento do programa. Veremos posteriormente as regras específicas para este intercâmbio. Por hora é importante saber que existe essa possibilidade, e que o significado sintático destes demarcadores não é idêntico entre Scala e Java.

Outra característica de Scala é a não obrigatoriedade do uso do ponto para a chamada de métodos de um objeto, quando a ausência deste não gera ambiguidade. Assim, nosso exemplo acima poderia ter estas linhas substituidas sem prejuízo ao programa:

val ordenados = alunosTurmaB.sorted
println("Alunos da turma B: " + ordenados.mkString(", "))

É igual a :
val ordenados = alunosTurmaB sorted;
println("Alunos da turma B: " + ordenados mkString(", "))

Repare que removemos o ponto após alunosTurmaB e entre ordenados e mkString. No entanto adicionamos um ponto-e-vírgula após alunosTurmaB sorted. Quando removemos o ponto, o compilador busca detectar a qual objeto estamos aplicando o método que segue. Neste caso o ponto-e-vírgula torna-se obrigatório, pois o compilador considera que a linha terminada por sorted continua com println na linha seguinte. Como a chamada de objeto.metodo pode ser objeto metodo o compilador não soube inferir se println pertencia ao retorno de sorted ou não. Este auxílio de sintaxe torna os programas mais fáceis de serem lidos ao não inserirem pausas desnecessárias entre “substantivos”, predicados e verbos, mas as eventuais ambiguidades devem ser eliminadas conforme nesse exemplo. Normalmente, sempre que a expressão não tiver interpretação dúbia, Scala permite omitir símbolos desnecessários de modo a manter o código o mais limpo possível.

Nos exemplos acima, o ponto-e-vírgula só foi usado para retirar a ambiguidade de uma linha. Note que em todas as outras este foi omitido. Desta forma torna-se evidente que este caractere só é exigido para separar expressões, e não para terminar expressões. Em caso de ambiguidade ou de mais de uma expressão por linha, o ponto-e-vírgula torna-se necessário. Onde o compilador for capaz de inferir o significado, o delimitador é opcional.

Scala permite este tipo de limpeza de código em diversas partes da linguagem. Por exemplo funções que não recebem parâmetros podem ter os parenteses omitidos, como foi ilustrado pelo exemplo de sorted. Este é um método de List mas que foi chamado sem parenteses em alunosTurmaB sorted que equivale a alunosTurmaB.sorted().

Scala é 100% Orientada a Objetos
Programadores Java devem se acostumar a tratar “tipos primitivos” e “objetos” de forma diferente. Tipos primitivos não são instanciados, são definidos e tem valores atribuidos diretamente, e seus valores são copiados antes de serem passados a outras funções. Já objetos em Java são denominados tipos referenciais pois referências(análogas aos apontadores) são passadas adiante, não uma cópia de seu valor.

Scala elimina esta diferença e permite apenas tipos referenciados: tudo em Scala é um objeto. Ao usarmos o número 10, este é uma referência a uma instância de Int. Assim, podemos chamar métodos desta classe diretamente ao número 10 como em println(10 toString()).

Como o compilador sabe que o número 10 deve ser transformado em Int? Esta funcionalidade é chamada “autoboxing”. Por hora basta saber que Scala é acompanhada de uma biblioteca de conversões deste tipo, onde o compilador procura a melhor conversão de tipos quando esses não são explicitados. Mais adiante falaremos mais desta funcionalidade. Lembrando que Scala é fortemente tipada, ou seja, sempre haverá um tipo para cada valor, mesmo que este tenha sido inferido pelo compilador.

val vs. var
Para encerrar este primeiro tutorial, falaremos rapidamente dos modificadores val e var. Ao definirmos um nome como val, seu valor torna-se imutável durante toda a execução do programa. Ou seja, usar o literal 10 e val dez = 10 tem o mesmo efeito: ambos são valores imutáveis. No entanto, ao declarar uma variável com var esta se comportará como uma variável não final em Java e poderá ser novamente definida: var dez = 10; dez = 11; //há algo errado aqui.

Quando buscamos escrever código estritamente funcional, a presença da palavra-chave var normalmente indica a presença de código imperativo. Resultados intermediários de programas funcionais devem usar apenas val.

Assim encerramos nosso passeio relâmpago pela linguagem Scala. Em breve falaremos em mais detalhe de muitas destas funcionalidades.

Links
Site oficial da linguagem Scala
Scala SBT
Typesafe – Empresa de Martin Odersky e outros idealizadores de Scala

Standard