Scala (linguagem de programação) - Scala (programming language)
Paradigma | Multi-paradigma : concorrente , funcional , imperativo , orientado a objetos |
---|---|
Projetado por | Martin Odersky |
Desenvolvedor | Laboratório de Métodos de Programação da École Polytechnique Fédérale de Lausanne |
Apareceu pela primeira vez | 20 de janeiro de 2004 |
Versão estável | |
Versão de visualização | |
Disciplina de digitação | Inferido , estático , forte , estrutural |
Linguagem de implementação | Scala |
Plataforma |
Máquina virtual Java (JVM) JavaScript ( Scala.js ) LLVM ( Scala Native ) (experimental) |
Licença | Apache 2.0 |
Extensões de nome de arquivo | .scala, .sc |
Local na rede Internet | www |
Influenciado por | |
Common Lisp , Eiffel , Erlang , F # , Haskell , Java , OCaml , Oz , Pizza , Scheme , Smalltalk , ML padrão | |
Influenciado | |
Ceilão , Cinzel , Fantom , F # , Kotlin , Laço , Vermelho | |
|
Scala ( / s k ɑː l ɑː / Skah -lah ) é um forte estaticamente digitado linguagem de programação de propósito geral que suporta tanto de programação orientada a objectos e a programação funcional . Projetado para ser conciso, muitas das decisões de design do Scala visam responder às críticas ao Java .
O código-fonte do Scala pode ser compilado em bytecode Java e executado em uma máquina virtual Java (JVM). Scala fornece interoperabilidade de linguagem com Java para que as bibliotecas escritas em qualquer uma das linguagens possam ser referenciadas diretamente no código Scala ou Java. Como Java, Scala é orientada para o objecto , e utiliza uma sintaxe denominado encaracolado-cinta , que é semelhante à linguagem C . Desde Scala 3, também existe a opção de usar a regra de desvio (recuo) para estruturar blocos , e seu uso é recomendado. Martin Odersky disse que esta acabou sendo a mudança mais produtiva introduzida no Scala 3.
Ao contrário do Java, Scala tem muitos recursos de linguagens de programação funcionais como Scheme , Standard ML e Haskell , incluindo currying , imutabilidade , avaliação lenta e correspondência de padrões . Ele também possui um sistema de tipo avançado que suporta tipos de dados algébricos , covariância e contravariância , tipos de ordem superior (mas não tipos de classificação superior ) e tipos anônimos . Outros recursos do Scala não presentes no Java incluem sobrecarga de operador , parâmetros opcionais, parâmetros nomeados e strings brutas . Por outro lado, um recurso do Java que não está no Scala são as exceções verificadas , o que tem se mostrado controverso.
O nome Scala é um portmanteau de escalabilidade e linguagem , o que significa que foi projetado para crescer com as demandas de seus usuários.
História
O design do Scala começou em 2001 na École Polytechnique Fédérale de Lausanne (EPFL) (em Lausanne , Suíça ) por Martin Odersky . Ele deu sequência ao trabalho no Funnel, uma linguagem de programação que combina ideias de programação funcional e redes de Petri . Odersky trabalhou anteriormente no Java genérico e no javac , o compilador Java da Sun.
Após um lançamento interno no final de 2003, Scala foi lançado publicamente no início de 2004 na plataforma Java . Uma segunda versão (v2.0) veio em março de 2006.
Em 17 de janeiro de 2011, a equipe do Scala ganhou uma bolsa de pesquisa de mais de € 2,3 milhões do Conselho Europeu de Pesquisa de cinco anos . Em 12 de maio de 2011, Odersky e colaboradores lançaram a Typesafe Inc. (mais tarde renomeada Lightbend Inc. ), uma empresa para fornecer suporte comercial, treinamento e serviços para Scala. A Typesafe recebeu um investimento de US $ 3 milhões em 2011 da Greylock Partners .
Plataformas e licença
Scala é executado na plataforma Java ( máquina virtual Java ) e é compatível com programas Java existentes . Como os aplicativos Android são normalmente escritos em Java e traduzidos de bytecode Java para bytecode Dalvik (que pode ser posteriormente traduzido para código de máquina nativo durante a instalação) quando empacotados, a compatibilidade de Java do Scala o torna adequado para desenvolvimento Android, mais ainda quando uma abordagem funcional é preferível.
A distribuição de software Scala de referência, incluindo compilador e bibliotecas, é lançada sob a licença Apache .
Outros compiladores e destinos
Scala.js é um compilador Scala que compila para JavaScript, tornando possível escrever programas Scala que podem ser executados em navegadores da web ou Node.js . O compilador, em desenvolvimento desde 2013, foi anunciado como não mais experimental em 2015 (v0.6). A versão v1.0.0-M1 foi lançada em junho de 2018 e a versão 1.1.1 em setembro de 2020.
Scala Native é um compilador Scala que visa a infraestrutura do compilador LLVM para criar código executável que usa um tempo de execução gerenciado leve, que usa o coletor de lixo Boehm . O projeto é liderado por Denys Shabalin e teve seu primeiro lançamento, 0.1, em 14 de março de 2017. O desenvolvimento do Scala Native começou em 2015 com o objetivo de ser mais rápido do que a compilação just-in-time para o JVM, eliminando a compilação inicial em tempo de execução de código e também fornecendo a capacidade de chamar rotinas nativas diretamente.
Um compilador Scala de referência voltado para o .NET Framework e seu Common Language Runtime foi lançado em junho de 2004, mas foi oficialmente descartado em 2012.
Exemplos
Exemplo de "Hello World"
O programa Hello World escrito em Scala tem este formato:
object HelloWorld extends App {
println("Hello, World!")
}
Ao contrário do aplicativo autônomo Hello World para Java , não há declaração de classe e nada é declarado como estático; um objeto singleton criado com a palavra-chave do objeto é usado em seu lugar.
Quando o programa é armazenado no arquivo HelloWorld.scala , o usuário o compila com o comando:
$ scalac HelloWorld.scala
e o executa com
$ scala HelloWorld
Isso é análogo ao processo de compilar e executar o código Java. Na verdade, o modelo de compilação e execução do Scala é idêntico ao do Java, tornando-o compatível com ferramentas de construção Java, como o Apache Ant .
Uma versão mais curta do programa Scala "Hello World" é:
println("Hello, World!")
Scala inclui um shell interativo e suporte a scripts. Salvo em um arquivo denominado HelloWorld2.scala
, pode ser executado como um script usando o comando:
$ scala HelloWorld2.scala
Os comandos também podem ser inseridos diretamente no interpretador Scala, usando a opção -e :
$ scala -e 'println("Hello, World!")'
As expressões podem ser inseridas interativamente no REPL :
$ scala
Welcome to Scala 2.12.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.
scala> List(1, 2, 3).map(x => x * x)
res0: List[Int] = List(1, 4, 9)
scala>
Exemplo básico
O exemplo a seguir mostra as diferenças entre a sintaxe Java e Scala. A função mathFunction pega um inteiro, o eleva ao quadrado e, em seguida, adiciona a raiz cúbica desse número ao log natural desse número, retornando o resultado (ou seja, ):
// Java:
int mathFunction(int num) {
int numSquare = num*num;
return (int) (Math.cbrt(numSquare) +
Math.log(numSquare));
}
|
|
// Scala: Direct conversion from Java
// no import needed; scala.math
// already imported as `math`
def mathFunction(num: Int): Int = {
var numSquare: Int = num*num
return (math.cbrt(numSquare) + math.log(numSquare)).
asInstanceOf[Int]
}
|
// Scala: More idiomatic
// Uses type inference, omits `return` statement,
// uses `toInt` method, declares numSquare immutable
import math._
def mathFunction(num: Int) = {
val numSquare = num*num
(cbrt(numSquare) + log(numSquare)).toInt
}
|
Algumas diferenças sintáticas neste código são:
- Scala não requer ponto-e-vírgula para encerrar as instruções.
- Os tipos de valor são capitalizados: em
Int, Double, Boolean
vez deint, double, boolean
. - Parâmetros e tipos de retorno siga, como em Pascal , em vez de preceder como em C .
- Os métodos devem ser precedidos por
def
. - Variáveis locais ou de classe devem ser precedidas por
val
(indica uma variável imutável ) ouvar
(indica uma variável mutável ). - O
return
operador é desnecessário em uma função (embora permitido); o valor da última instrução ou expressão executada é normalmente o valor da função. - Em vez do operador de cast Java
(Type) foo
, Scala usafoo.asInstanceOf[Type]
, ou uma função especializada, comotoDouble
outoInt
. - Em vez de Java
import foo.*;
, Scala usaimport foo._
. - Função ou método
foo()
também pode ser chamado como justofoo
; métodothread.send(signo)
também pode ser chamado como justothread send signo
; e o métodofoo.toString()
também pode ser chamado de justofoo toString
.
Esses relaxamentos sintáticos são projetados para permitir o suporte para idiomas específicos de domínio .
Algumas outras diferenças sintáticas básicas:
- As referências de array são escritas como chamadas de função, por exemplo, em
array(i)
vez dearray[i]
. (Internamente no Scala, o primeiro se expande em array.apply (i) que retorna a referência) - Tipos genéricos são escritos como, por exemplo, em
List[String]
vez de JavaList<String>
. - Em vez do pseudo-tipo
void
, Scala tem a classe singleton realUnit
(veja abaixo).
Exemplo com aulas
O exemplo a seguir compara a definição de classes em Java e Scala.
// Java:
public class Point {
private final double x, y;
public Point(final double x, final double y) {
this.x = x;
this.y = y;
}
public Point(
final double x, final double y,
final boolean addToGrid
) {
this(x, y);
if (addToGrid)
grid.addToGrid(this);
}
public Point() {
this(0.0, 0.0);
}
public double getX() {
return x;
}
public double getY() {
return y;
}
double distanceToPoint(final Point other) {
return distanceBetweenPoints(x, y,
other.x, other.y);
}
private static Grid grid = new Point();
static double distanceBetweenPoints(
final double x1, final double y1,
final double x2, final double y2
) {
return Math.hypot(x1 - x2, y1 - y2);
}
}
|
// Scala
class Point(
val x: Double, val y: Double,
addToGrid: Boolean = false
) {
import Point._
if (addToGrid)
grid.addToGrid(this)
def this() = this(0.0, 0.0)
def distanceToPoint(other: Point) =
distanceBetweenPoints(x, y, other.x, other.y)
}
object Point {
private val grid = new Point()
def distanceBetweenPoints(x1: Double, y1: Double,
x2: Double, y2: Double) = {
math.hypot(x1 - x2, y1 - y2)
}
}
|
O código acima mostra algumas das diferenças conceituais entre o manuseio de classes do Java e do Scala:
- Scala não possui variáveis ou métodos estáticos. Em vez disso, ele possui objetos singleton , que são essencialmente classes com apenas uma instância. Objetos singleton são declarados usando em
object
vez declass
. É comum colocar variáveis e métodos estáticos em um objeto singleton com o mesmo nome do nome da classe, que é então conhecido como um objeto companheiro . (A classe subjacente para o objeto singleton tem um$
anexo. Portanto, paraclass Foo
com o objeto companheiroobject Foo
, sob o capô há uma classeFoo$
contendo o código do objeto companheiro, e um objeto desta classe é criado, usando o padrão singleton .) - No lugar dos parâmetros do construtor, Scala possui parâmetros de classe , que são colocados na classe, semelhantes aos parâmetros de uma função. Quando declarados com um modificador
val
ouvar
, os campos também são definidos com o mesmo nome e inicializados automaticamente a partir dos parâmetros da classe. (Nos bastidores, o acesso externo a campos públicos sempre passa pelos métodos acessador (getter) e mutator (setter), que são criados automaticamente. A função acessador tem o mesmo nome do campo, por isso é desnecessário no exemplo acima declare explicitamente os métodos do acessador.) Observe que os construtores alternativos também podem ser declarados, como em Java. O código que iria para o construtor padrão (diferente de inicializar as variáveis de membro) vai diretamente no nível de classe. - A visibilidade padrão no Scala é
public
.
Recursos (com referência a Java)
Scala tem o mesmo modelo de compilação que Java e C # , ou seja, compilação separada e carregamento dinâmico de classes , para que o código Scala possa chamar bibliotecas Java.
As características operacionais do Scala são as mesmas do Java. O compilador Scala gera código de bytes quase idêntico ao gerado pelo compilador Java. Na verdade, o código Scala pode ser descompilado em código Java legível, com exceção de certas operações do construtor. Para a máquina virtual Java (JVM), o código Scala e o código Java são indistinguíveis. A única diferença é uma biblioteca de tempo de execução extra scala-library.jar
,.
Scala adiciona um grande número de recursos em comparação com Java e tem algumas diferenças fundamentais em seu modelo subjacente de expressões e tipos, o que torna a linguagem teoricamente mais limpa e elimina vários casos esquivos em Java. Da perspectiva do Scala, isso é praticamente importante porque vários recursos adicionados ao Scala também estão disponíveis em C #.
Flexibilidade sintática
Conforme mencionado acima, Scala tem uma boa dose de flexibilidade sintática, em comparação com Java. A seguir estão alguns exemplos:
- Os pontos-e-vírgulas são desnecessários; as linhas são unidas automaticamente se começarem ou terminarem com um token que normalmente não pode vir nesta posição, ou se houver parênteses ou colchetes não fechados.
- Qualquer método pode ser usado como um operador infixo, por exemplo,
"%d apples".format(num)
e"%d apples" format num
são equivalentes. Na verdade, os operadores aritméticos gostam+
e<<
são tratados como quaisquer outros métodos, uma vez que os nomes das funções podem consistir em sequências de símbolos arbitrários (com algumas exceções feitas para itens como parênteses, colchetes e chaves que devem ser tratados especialmente); o único tratamento especial a que esses métodos nomeados por símbolo são submetidos diz respeito ao tratamento da precedência. - Métodos
apply
eupdate
têm formas sintáticas curtas.foo()
—Ondefoo
está um valor (objeto singleton ou instância de classe) —é abreviação defoo.apply()
, efoo() = 42
é abreviação defoo.update(42)
. Da mesma forma,foo(42)
é abreviação defoo.apply(42)
efoo(4) = 2
é abreviação defoo.update(4, 2)
. Isso é usado para classes de coleção e se estende a muitos outros casos, como células STM . - Scala distingue entre os métodos sem parênteses (
def foo = 42
) e osdef foo() = 42
métodos com parênteses vazios ( ). Ao chamar um método de parênteses vazios, os parênteses podem ser omitidos, o que é útil ao chamar bibliotecas Java que não conhecem essa distinção, por exemplo, usando emfoo.toString
vez defoo.toString()
. Por convenção, um método deve ser definido com parênteses vazios quando executa efeitos colaterais . - Os nomes de métodos que terminam em dois pontos (
:
) esperam o argumento do lado esquerdo e o receptor do lado direito. Por exemplo, o4 :: 2 :: Nil
é o mesmo queNil.::(2).::(4)
a primeira forma correspondendo visualmente ao resultado (uma lista com o primeiro elemento 4 e o segundo elemento 2). - As variáveis do corpo da classe podem ser implementadas de forma transparente como métodos getter e setter separados. Pois
trait FooLike { var bar: Int }
, uma implementação pode ser . O site de chamada ainda poderá usar um conciso .object Foo extends FooLike { private var x = 0; def bar = x; def bar_=(value: Int) { x = value }} } }
foo.bar = 42
- O uso de chaves em vez de parênteses é permitido nas chamadas de método. Isso permite implementações de bibliotecas puras de novas estruturas de controle. Por exemplo,
breakable { ... if (...) break() ... }
parece quebreakable
era uma palavra-chave definida pelo idioma, mas na verdade é apenas um método que usa um argumento de conversão . Métodos que usam thunks ou funções geralmente os colocam em uma segunda lista de parâmetros, permitindo misturar parênteses e chaves com a sintaxe:Vector.fill(4) { math.random }
é o mesmo queVector.fill(4)(math.random)
. A variante das chaves permite que a expressão se estenda por várias linhas. - For-expression (explicado mais adiante) pode acomodar qualquer tipo que defina métodos monádicos como
map
,flatMap
efilter
.
Por si só, essas podem parecer escolhas questionáveis, mas coletivamente servem ao propósito de permitir que linguagens específicas de domínio sejam definidas em Scala sem a necessidade de estender o compilador. Por exemplo, a sintaxe especial de Erlang para enviar uma mensagem a um ator, ou seja, actor ! message
pode ser (e é) implementada em uma biblioteca Scala sem a necessidade de extensões de linguagem.
Sistema de tipo unificado
Java faz uma distinção nítida entre tipos primitivos (por exemplo, int
e boolean
) e tipos de referência (qualquer classe ). Apenas os tipos de referência fazem parte do esquema de herança, derivando de java.lang.Object
. Em Scala, todos os tipos herdam de uma classe de nível superior Any
, cujos filhos imediatos são AnyVal
(tipos de valor, como Int
e Boolean
) e AnyRef
(tipos de referência, como em Java). Isso significa que a distinção Java entre tipos primitivos e tipos em caixa (por exemplo, int
vs. Integer
) não está presente no Scala; boxing e unboxing são completamente transparentes para o usuário. Scala 2.10 permite que novos tipos de valores sejam definidos pelo usuário.
For-expressões
Em vez dos loops " foreach " do Java para fazer um loop por meio de um iterador, Scala tem for
-expressions, que são semelhantes às compreensões de lista em linguagens como Haskell, ou uma combinação de compreensões de lista e expressões geradoras em Python . For-expressões que usam a yield
palavra - chave permitem que uma nova coleção seja gerada iterando sobre uma existente, retornando uma nova coleção do mesmo tipo. Eles são traduzidas pelo compilador em uma série de map
, flatMap
e filter
chamadas. Onde yield
não é usado, o código se aproxima de um loop de estilo imperativo, traduzindo para foreach
.
Um exemplo simples é:
val s = for (x <- 1 to 25 if x*x > 50) yield 2*x
O resultado de sua execução é o seguinte vetor:
Vector(16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50)
(Observe que a expressão 1 to 25
não é uma sintaxe especial. O método to
é definido na biblioteca Scala padrão como um método de extensão em inteiros, usando uma técnica conhecida como conversões implícitas que permite que novos métodos sejam adicionados aos tipos existentes.)
Um exemplo mais complexo de iteração em um mapa é:
// Given a map specifying Twitter users mentioned in a set of tweets,
// and number of times each user was mentioned, look up the users
// in a map of known politicians, and return a new map giving only the
// Democratic politicians (as objects, rather than strings).
val dem_mentions = for {
(mention, times) <- mentions
account <- accounts.get(mention)
if account.party == "Democratic"
} yield (account, times)
Expressão (mention, times) <- mentions
é um exemplo de correspondência de padrões (veja abaixo). A iteração em um mapa retorna um conjunto de tuplas de valor-chave e a correspondência de padrões permite que as tuplas sejam facilmente desestruturadas em variáveis separadas para a chave e o valor. Da mesma forma, o resultado da compreensão também retorna tuplas de valor-chave, que são automaticamente construídas de volta em um mapa porque o objeto de origem (da variável mentions
) é um mapa. Observe que se, em mentions
vez disso, mantivesse uma lista, conjunto, array ou outra coleção de tuplas, exatamente o mesmo código acima resultaria em uma nova coleção do mesmo tipo.
Tendências funcionais
Ao mesmo tempo que oferece suporte a todos os recursos orientados a objetos disponíveis em Java (e, na verdade, aumenta-os de várias maneiras), o Scala também fornece um grande número de recursos que normalmente são encontrados apenas em linguagens de programação funcionais . Juntos, esses recursos permitem que os programas Scala sejam escritos em um estilo quase totalmente funcional e também permitem que estilos funcionais e orientados a objetos sejam misturados.
Exemplos são:
- Sem distinção entre declarações e expressões
- Inferência de tipo
- Funções anônimas com captura de semântica (ou seja, fechamentos )
- Variáveis e objetos imutáveis
- Avaliação preguiçosa
- Continuações delimitadas (desde 2.8)
- Funções de ordem superior
- Funções aninhadas
- Escovando
- Correspondência de padrões
- Tipos de dados algébricos (por meio de classes de caso )
- Tuplas
Tudo é uma expressão
Ao contrário de C ou Java, mas semelhante a linguagens como Lisp , Scala não faz distinção entre instruções e expressões . Todas as declarações são, na verdade, expressões que avaliam algum valor. Funções que seriam declaradas como retornando void
em C ou Java, e instruções como while
essa logicamente não retornam um valor, são consideradas em Scala como retornando o tipo Unit
, que é um tipo singleton , com apenas um objeto desse tipo. Funções e operadores que nunca retornam (por exemplo, o throw
operador ou uma função que sempre sai não localmente usando uma exceção) logicamente têm tipo de retorno Nothing
, um tipo especial que não contém objetos; ou seja, um tipo inferior , ou seja, uma subclasse de todos os tipos possíveis. (Isso, por sua vez, torna o tipo Nothing
compatível com todos os tipos, permitindo que a inferência de tipo funcione corretamente.)
Da mesma forma, uma if-then-else
"declaração" é na verdade uma expressão que produz um valor, ou seja, o resultado da avaliação de um dos dois ramos. Isso significa que esse bloco de código pode ser inserido onde quer que uma expressão seja desejada, eliminando a necessidade de um operador ternário em Scala:
// Java:
int hexDigit = x >= 10 ? x + 'A' - 10 : x + '0';
|
// Scala:
val hexDigit = if (x >= 10) x + 'A' - 10 else x + '0'
|
Por motivos semelhantes, as return
instruções são desnecessárias no Scala e, na verdade, são desencorajadas. Como no Lisp, a última expressão em um bloco de código é o valor desse bloco de código e, se o bloco de código for o corpo de uma função, ele será retornado pela função.
Para deixar claro que todas as funções são expressões, mesmo os métodos que retornam Unit
são escritos com um sinal de igual
def printValue(x: String): Unit = {
println("I ate a %s".format(x))
}
ou de forma equivalente (com inferência de tipo e omitindo as chaves desnecessárias):
def printValue(x: String) = println("I ate a %s" format x)
Inferência de tipo
Devido à inferência de tipo , o tipo de variáveis, valores de retorno de função e muitas outras expressões podem ser omitidos, conforme o compilador pode deduzir. Os exemplos são val x = "foo"
(para uma constante imutável ou objeto imutável ) ou var x = 1.5
(para uma variável cujo valor pode ser alterado posteriormente). A inferência de tipo em Scala é essencialmente local, em contraste com o algoritmo Hindley-Milner mais global usado em Haskell , ML e outras linguagens mais puramente funcionais. Isso é feito para facilitar a programação orientada a objetos. O resultado é que certos tipos ainda precisam ser declarados (mais notavelmente, parâmetros de função e os tipos de retorno de funções recursivas ), por exemplo
def formatApples(x: Int) = "I ate %d apples".format(x)
ou (com um tipo de retorno declarado para uma função recursiva)
def factorial(x: Int): Int =
if (x == 0)
1
else
x*factorial(x - 1)
Funções anônimas
No Scala, funções são objetos e existe uma sintaxe conveniente para especificar funções anônimas . Um exemplo é a expressão x => x < 2
, que especifica uma função com um parâmetro, que compara seu argumento para ver se é menor que 2. É equivalente à forma Lisp (lambda (x) (< x 2))
. Observe que nem o tipo de x
nem o tipo de retorno precisam ser especificados explicitamente e geralmente podem ser inferidos por inferência de tipo ; mas eles podem ser especificados explicitamente, por exemplo, como (x: Int) => x < 2
ou mesmo (x: Int) => (x < 2): Boolean
.
As funções anônimas se comportam como fechamentos verdadeiros, pois capturam automaticamente quaisquer variáveis que estejam lexicamente disponíveis no ambiente da função envolvente. Essas variáveis estarão disponíveis mesmo após o retorno da função envolvente e, ao contrário do caso das classes internas anônimas do Java, não precisam ser declaradas como finais. (É até possível modificar essas variáveis se elas forem mutáveis, e o valor modificado estará disponível na próxima vez que a função anônima for chamada.)
Uma forma ainda mais curta de função anônima usa variáveis de espaço reservado : Por exemplo, o seguinte:
list map { x => sqrt(x) }
pode ser escrito de forma mais concisa como
list map { sqrt(_) }
ou mesmo
list map sqrt
Imutabilidade
Scala impõe uma distinção entre variáveis imutáveis e mutáveis. Variáveis mutáveis são declaradas usando a var
palavra - chave e os valores imutáveis são declarados usando a val
palavra - chave. Uma variável declarada com a val
palavra - chave não pode ser reatribuída da mesma forma que uma variável declarada com a final
palavra - chave não pode ser reatribuída em Java. val
's são apenas superficialmente imutáveis, ou seja, um objeto referenciado por um val não tem garantia de ser imutável.
No entanto, classes imutáveis são incentivadas por convenção e a biblioteca padrão Scala fornece um rico conjunto de classes de coleção imutáveis . Scala fornece variantes mutáveis e imutáveis da maioria das classes de coleção, e a versão imutável é sempre usada, a menos que a versão mutável seja importada explicitamente. As variantes imutáveis são estruturas de dados persistentes que sempre retornam uma cópia atualizada de um objeto antigo, em vez de atualizar o objeto antigo destrutivamente no local. Um exemplo disso são as listas vinculadas imutáveis, nas quais adicionar um elemento a uma lista é feito retornando um novo nó de lista que consiste no elemento e uma referência ao final da lista. Anexar um elemento a uma lista só pode ser feito adicionando todos os elementos da lista antiga a uma nova lista apenas com o novo elemento. Da mesma forma, inserir um elemento no meio de uma lista copiará a primeira metade da lista, mas manterá uma referência à segunda metade da lista. Isso é chamado de compartilhamento estrutural. Isso permite uma simultaneidade muito fácil - nenhum bloqueio é necessário, pois nenhum objeto compartilhado é modificado.
Avaliação preguiçosa (não estrita)
A avaliação é estrita ("ansiosa") por padrão. Em outras palavras, o Scala avalia as expressões assim que elas estão disponíveis, ao invés de quando necessário. Porém, é possível declarar uma variável não estrita ("preguiçosa") com a lazy
palavra - chave, significando que o código para produzir o valor da variável não será avaliado até a primeira vez que a variável for referenciada. Também existem coleções não estritas de vários tipos (como o tipo Stream
, uma lista vinculada não estrita) e qualquer coleção pode ser tornada não estrita com o view
método. Coleções não estritas fornecem um bom ajuste semântico para coisas como dados produzidos pelo servidor, onde a avaliação do código para gerar elementos posteriores de uma lista (que por sua vez aciona uma solicitação para um servidor, possivelmente localizado em outro lugar na web) apenas acontece quando os elementos são realmente necessários.
Recursão de cauda
Linguagens de programação funcional geralmente fornecem otimização de chamada final para permitir o uso extensivo de recursão sem problemas de estouro de pilha . As limitações no bytecode Java complicam a otimização da chamada final na JVM. Em geral, uma função que chama a si mesma com uma chamada final pode ser otimizada, mas as funções recursivas mutuamente não. Trampolins foram sugeridos como uma solução alternativa. O suporte para trampolim foi fornecido pela biblioteca Scala com o objeto scala.util.control.TailCalls
desde Scala 2.8.0 (lançado em 14 de julho de 2010). Uma função pode opcionalmente ser anotada com @tailrec
, nesse caso ela não será compilada a menos que seja recursiva na cauda.
Classes de caso e correspondência de padrões
Scala tem suporte integrado para correspondência de padrões , que pode ser considerada uma versão mais sofisticada e extensível de uma instrução switch , onde tipos de dados arbitrários podem ser correspondidos (em vez de apenas tipos simples como inteiros, booleanos e strings), incluindo arbitrários aninhamento. É fornecido um tipo especial de classe conhecido como classe de caso , que inclui suporte automático para correspondência de padrões e pode ser usado para modelar os tipos de dados algébricos usados em muitas linguagens de programação funcionais. (Da perspectiva de Scala, uma classe de caso é simplesmente uma classe normal para a qual o compilador adiciona automaticamente certos comportamentos que também podem ser fornecidos manualmente, por exemplo, definições de métodos que fornecem comparações profundas e hashing, e desestruturação de uma classe de caso em seu construtor parâmetros durante a correspondência de padrões.)
Um exemplo de definição do algoritmo de classificação rápida usando correspondência de padrões é este:
def qsort(list: List[Int]): List[Int] = list match {
case Nil => Nil
case pivot :: tail =>
val (smaller, rest) = tail.partition(_ < pivot)
qsort(smaller) ::: pivot :: qsort(rest)
}
A ideia aqui é particionar uma lista nos elementos menores do que um pivô e os elementos não menos, ordenar recursivamente cada parte e colar os resultados junto com o pivô intermediário. Isso usa a mesma estratégia de dividir e conquistar de mergesort e outros algoritmos de classificação rápida.
O match
operador é usado para fazer correspondência de padrões no objeto armazenado em list
. Cada case
expressão é tentada separadamente para ver se ela corresponde, e a primeira combinação determina o resultado. Nesse caso, Nil
corresponde apenas ao objeto literal Nil
, mas pivot :: tail
corresponde a uma lista não vazia e, simultaneamente, destrói a lista de acordo com o padrão fornecido. Neste caso, o código associado terá acesso a uma variável local chamada pivot
segurando o topo da lista e outra variável tail
segurando o final da lista. Observe que essas variáveis são somente leitura e semanticamente muito semelhantes às associações de variáveis estabelecidas usando o let
operador em Lisp e Scheme.
A correspondência de padrões também ocorre em declarações de variáveis locais. Neste caso, o valor de retorno da chamada para tail.partition
é uma tupla - neste caso, duas listas. (As tuplas diferem de outros tipos de contêineres, por exemplo, listas, porque são sempre de tamanho fixo e os elementos podem ser de tipos diferentes - embora aqui sejam os mesmos.) A correspondência de padrões é a maneira mais fácil de buscar as duas partes de a tupla.
O formulário _ < pivot
é uma declaração de uma função anônima com uma variável de espaço reservado; veja a seção acima sobre funções anônimas.
Os operadores de lista ::
(que adiciona um elemento no início de uma lista, semelhante a cons
em Lisp e Scheme) e :::
(que anexa duas listas, semelhante a append
em Lisp e Scheme) aparecem. Apesar das aparências, não há nada "embutido" em nenhum desses operadores. Conforme especificado acima, qualquer sequência de símbolos pode servir como nome de função, e um método aplicado a um objeto pode ser escrito no estilo " infixo " sem o ponto ou parênteses. A linha acima está escrita:
qsort(smaller) ::: pivot :: qsort(rest)
também poderia ser escrito assim:
qsort(rest).::(pivot).:::(qsort(smaller))
em notação de chamada de método mais padrão. (Os métodos que terminam com dois pontos são associativos à direita e se ligam ao objeto à direita.)
Funções parciais
No exemplo de correspondência de padrões acima, o corpo do match
operador é uma função parcial , que consiste em uma série de case
expressões, com a primeira expressão de correspondência prevalecendo, semelhante ao corpo de uma instrução switch . Funções parciais também são usadas na parte de tratamento de exceções de uma try
instrução:
try {
...
} catch {
case nfe:NumberFormatException => { println(nfe); List(0) }
case _ => Nil
}
Finalmente, uma função parcial pode ser usada sozinha, e o resultado de chamá-la é equivalente a fazer um match
sobre ela. Por exemplo, o código anterior para quicksort pode ser escrito assim:
val qsort: List[Int] => List[Int] = {
case Nil => Nil
case pivot :: tail =>
val (smaller, rest) = tail.partition(_ < pivot)
qsort(smaller) ::: pivot :: qsort(rest)
}
Aqui, uma variável somente leitura é declarada cujo tipo é uma função de listas de inteiros para listas de inteiros e vincula-a a uma função parcial. (Observe que o único parâmetro da função parcial nunca é declarado ou nomeado explicitamente.) No entanto, ainda podemos chamar essa variável exatamente como se fosse uma função normal:
scala> qsort(List(6,2,5,9))
res32: List[Int] = List(2, 5, 6, 9)
Extensões orientadas a objetos
Scala é uma linguagem puramente orientada a objetos, no sentido de que todo valor é um objeto . Tipos de dados e comportamentos de objetos são descritos por classes e características . As abstrações de classe são estendidas por subclasses e por um mecanismo de composição flexível baseado em mixin para evitar os problemas de herança múltipla .
Traits são substitutos do Scala para as interfaces Java . As interfaces nas versões Java abaixo de 8 são altamente restritas, podendo conter apenas declarações de funções abstratas. Isso levou à crítica de que fornecer métodos de conveniência em interfaces é estranho (os mesmos métodos devem ser reimplementados em cada implementação) e estender uma interface publicada de uma maneira compatível com versões anteriores é impossível. As características são semelhantes às classes mixin no sentido de que têm quase todo o poder de uma classe abstrata regular, faltando apenas parâmetros de classe (o equivalente do Scala aos parâmetros do construtor de Java), uma vez que as características são sempre misturadas com uma classe. O super
operador se comporta especialmente nas características, permitindo que as características sejam encadeadas usando composição além da herança. O exemplo a seguir é um sistema de janela simples:
abstract class Window {
// abstract
def draw()
}
class SimpleWindow extends Window {
def draw() {
println("in SimpleWindow")
// draw a basic window
}
}
trait WindowDecoration extends Window { }
trait HorizontalScrollbarDecoration extends WindowDecoration {
// "abstract override" is needed here for "super()" to work because the parent
// function is abstract. If it were concrete, regular "override" would be enough.
abstract override def draw() {
println("in HorizontalScrollbarDecoration")
super.draw()
// now draw a horizontal scrollbar
}
}
trait VerticalScrollbarDecoration extends WindowDecoration {
abstract override def draw() {
println("in VerticalScrollbarDecoration")
super.draw()
// now draw a vertical scrollbar
}
}
trait TitleDecoration extends WindowDecoration {
abstract override def draw() {
println("in TitleDecoration")
super.draw()
// now draw the title bar
}
}
Uma variável pode ser declarada assim:
val mywin = new SimpleWindow with VerticalScrollbarDecoration with HorizontalScrollbarDecoration with TitleDecoration
O resultado da chamada mywin.draw()
é:
in TitleDecoration
in HorizontalScrollbarDecoration
in VerticalScrollbarDecoration
in SimpleWindow
Em outras palavras, a chamada para draw
primeiro executou o código em TitleDecoration
(o último traço misturado), depois (por meio das super()
chamadas) encadeado de volta para os outros traços mistos e, eventualmente, para o código em Window
, mesmo que nenhum dos traços tenha herdado de um ao outro . Isso é semelhante ao padrão do decorador , mas é mais conciso e menos sujeito a erros, pois não exige o encapsulamento explícito da janela pai, o encaminhamento explícito de funções cuja implementação não é alterada ou a inicialização em tempo de execução de relacionamentos de entidade . Em outras linguagens, um efeito semelhante poderia ser alcançado em tempo de compilação com uma longa cadeia linear de herança de implementação , mas com a desvantagem em comparação com Scala que uma cadeia de herança linear teria que ser declarada para cada combinação possível dos mix-ins.
Sistema de tipo expressivo
O Scala é equipado com um expressivo sistema de tipos estáticos que principalmente reforça o uso seguro e coerente de abstrações. O sistema de tipos, entretanto, não é sólido . Em particular, o sistema de tipos suporta:
- Classes e tipos abstratos como membros do objeto
- Tipos estruturais
- Tipos dependentes de caminho
- Tipos compostos
- Referências próprias digitadas explicitamente
- Classes genéricas
- Métodos polimórficos
- Limites de tipo superior e inferior
- Variância
- Anotação
- Visualizações
Scala é capaz de inferir tipos por uso. Isso torna a maioria das declarações de tipo estático opcionais. Os tipos estáticos não precisam ser declarados explicitamente, a menos que um erro do compilador indique a necessidade. Na prática, algumas declarações de tipo estático são incluídas para fins de clareza de código.
Enriquecimento de tipo
Uma técnica comum em Scala, conhecida como "enriquecer minha biblioteca" (originalmente denominada " cafetão minha biblioteca " por Martin Odersky em 2006; preocupações foram levantadas sobre esta frase devido às suas conotações negativas e imaturidade), permite que novos métodos sejam usados como se eles foram adicionados aos tipos existentes. Isso é semelhante ao conceito C # de métodos de extensão, mas mais poderoso, porque a técnica não se limita a adicionar métodos e pode, por exemplo, ser usada para implementar novas interfaces. No Scala, essa técnica envolve a declaração de uma conversão implícita do tipo que "recebe" o método para um novo tipo (normalmente, uma classe) que envolve o tipo original e fornece o método adicional. Se um método não puder ser encontrado para um determinado tipo, o compilador procura automaticamente qualquer conversão implícita aplicável para tipos que fornecem o método em questão.
Essa técnica permite que novos métodos sejam adicionados a uma classe existente usando uma biblioteca complementar de forma que apenas o código que importa a biblioteca adicional obtenha a nova funcionalidade e todos os outros códigos não sejam afetados.
O exemplo a seguir mostra o enriquecimento de tipo Int
com métodos isEven
e isOdd
:
object MyExtensions {
implicit class IntPredicates(i: Int) {
def isEven = i % 2 == 0
def isOdd = !isEven
}
}
import MyExtensions._ // bring implicit enrichment into scope
4.isEven // -> true
Importar os membros de MyExtensions
traz a conversão implícita para a classe de extensão IntPredicates
no escopo.
Simultaneidade
A biblioteca padrão do Scala inclui suporte para futuros e promessas , além das APIs de simultaneidade Java padrão. Originalmente, ele também incluiu suporte para o modelo de ator , que agora está disponível como uma plataforma open source separada Akka criado por Lightbend Inc. atores Akka podem ser distribuídos ou combinada com software de memória transacional ( transatores ). Implementações alternativas de processos sequenciais de comunicação (CSP) para passagem de mensagens baseadas em canal são objetos Scala de comunicação ou simplesmente via JCSP .
Um ator é como uma instância de thread com uma caixa de correio. Ele pode ser criado system.actorOf
substituindo o receive
método para receber mensagens e usando o método !
(ponto de exclamação) para enviar uma mensagem. O exemplo a seguir mostra um EchoServer que pode receber mensagens e depois imprimi-las.
val echoServer = actor(new Act {
become {
case msg => println("echo " + msg)
}
})
echoServer ! "hi"
Scala também vem com suporte embutido para programação paralela de dados na forma de Coleções Paralelas integradas em sua Biblioteca Padrão desde a versão 2.9.0.
O exemplo a seguir mostra como usar coleções paralelas para melhorar o desempenho.
val urls = List("https://scala-lang.org", "https://github.com/scala/scala")
def fromURL(url: String) = scala.io.Source.fromURL(url)
.getLines().mkString("\n")
val t = System.currentTimeMillis()
urls.par.map(fromURL(_)) // par returns parallel implementation of a collection
println("time: " + (System.currentTimeMillis - t) + "ms")
Além de futuros e promessas, suporte ao ator e paralelismo de dados , o Scala também oferece suporte à programação assíncrona com memória transacional de software e fluxos de eventos.
Computação de cluster
A solução de computação em cluster de código aberto mais conhecida escrita em Scala é o Apache Spark . Além disso, o Apache Kafka , a fila de mensagens de publicação / assinatura popular com Spark e outras tecnologias de processamento de fluxo, é escrito em Scala.
Testando
Existem várias maneiras de testar o código em Scala. ScalaTest oferece suporte a vários estilos de teste e pode ser integrado a estruturas de teste baseadas em Java. ScalaCheck é uma biblioteca semelhante ao QuickCheck de Haskell . specs2 é uma biblioteca para escrever especificações de software executável. ScalaMock fornece suporte para testar funções de alta ordem e curried. JUnit e TestNG são estruturas de teste populares escritas em Java.
Versões
Versão | Liberado | Recursos |
---|---|---|
1.0.0-b2 | 8 de dezembro de 2003 | _ |
1.1.0-b1 | 19 de fevereiro de 2004 |
|
1.1.1 | 23 de março de 2004 |
|
1.2.0 | 9 de junho de 2004 |
|
1.3.0 | 16 de setembro de 2004 |
|
1.4.0 | 20 de junho de 2005 |
|
2.0 | 12 de março de 2006 |
|
2.1.0 | 17 de março de 2006 |
|
2.1.8 | 23 de agosto de 2006 |
|
2.3.0 | 23 de novembro de 2006 |
|
2.4.0 | 9 de março de 2007 |
|
2.5.0 | 2 de maio de 2007 |
|
2.6.0 | 27 de julho de 2007 |
|
2.7.0 | 7 de fevereiro de 2008 |
|
2.8.0 | 14 de julho de 2010 |
|
2.9.0 | 12 de maio de 2011 |
|
2,10 | 4 de janeiro de 2013 |
Recursos experimentais
|
2.10.2 | 6 de junho de 2013 | _ |
2.10.3 | 1 de outubro de 2013 | _ |
2.10.4 | 18 de março de 2014 | _ |
2,10.5 | 5 de março de 2015 | _ |
2.11.0 | 21 de abril de 2014 |
|
2.11.1 | 20 de maio de 2014 | _ |
2.11.2 | 22 de julho de 2014 | _ |
2.11.4 | 31 de outubro de 2014 | _ |
2.11.5 | 8 de janeiro de 2015 | _ |
2.11.6 | 5 de março de 2015 | _ |
2.11.7 | 23 de junho de 2015 | _ |
2.11.8 | 8 de março de 2016 | _ |
2.11.11 | 18 de abril de 2017 | _ |
2.11.12 | 13 de novembro de 2017 | _ |
2.12.0 | 3 de novembro de 2016 |
|
2.12.1 | 5 de dezembro de 2016 | _ |
2.12.2 | 18 de abril de 2017 | _ |
2.12.3 | 26 de julho de 2017 | _ |
2.12.4 | 17 de outubro de 2017 | _ |
2,12.5 | 15 de março de 2018 | _ |
2.12.6 | 27 de abril de 2018 | _ |
2.12.7 | 27 de setembro de 2018 | _ |
2,12.8 | 4 de dezembro de 2018 |
|
2,12,9 | 5 de agosto de 2019 | _ |
2.12.10 | 10 de setembro de 2019 | _ |
2.12.11 | 16 de março de 2020 | _ |
2,12,12 | 13 de julho de 2020 | _ |
2.12.13 | 12 de janeiro de 2021 | _ |
2.13.0 | 11 de junho de 2019 |
|
2.13.1 | 18 de setembro de 2019 | _ |
2.13.2 | 22 de abril de 2020 | _ |
2.13.3 | 25 de junho de 2020 | _ |
2.13.4 | 19 de novembro de 2020 | _ |
2,13.5 | 22 de fevereiro de 2021 | _ |
3.0.0 | 13 de maio de 2021 | _ |
Comparação com outras linguagens JVM
Scala é freqüentemente comparado com Groovy e Clojure , duas outras linguagens de programação que também usam a JVM. Existem diferenças substanciais entre essas linguagens no sistema de tipos, na medida em que cada linguagem suporta programação orientada a objetos e funcional e na semelhança de sua sintaxe com a de Java.
Scala é tipado estaticamente , enquanto Groovy e Clojure são tipados dinamicamente . Isso torna o sistema de tipo mais complexo e difícil de entender, mas permite que quase todos os erros de tipo sejam detectados em tempo de compilação e pode resultar em uma execução significativamente mais rápida. Em contraste, a tipagem dinâmica requer mais testes para garantir a correção do programa e, portanto, é geralmente mais lenta, para permitir maior flexibilidade e simplicidade de programação. Com relação às diferenças de velocidade, as versões atuais de Groovy e Clojure permitem anotações de tipo opcionais para ajudar os programas a evitar a sobrecarga de digitação dinâmica em casos em que os tipos são praticamente estáticos. Essa sobrecarga é reduzida ainda mais ao usar versões recentes da JVM, que foi aprimorada com uma instrução dinâmica de chamada para métodos que são definidos com argumentos digitados dinamicamente. Esses avanços reduzem a lacuna de velocidade entre a tipagem estática e dinâmica, embora uma linguagem tipada estaticamente, como Scala, ainda seja a escolha preferida quando a eficiência de execução é muito importante.
Com relação aos paradigmas de programação, Scala herda o modelo orientado a objetos de Java e o estende de várias maneiras. O Groovy, embora também fortemente orientado a objetos, é mais focado na redução do detalhamento. Em Clojure, a programação orientada a objetos não é enfatizada, sendo a programação funcional o principal ponto forte da linguagem. Scala também possui muitos recursos de programação funcional, incluindo recursos encontrados em linguagens funcionais avançadas como Haskell , e tenta ser agnóstico entre os dois paradigmas, permitindo que o desenvolvedor escolha entre os dois paradigmas ou, mais frequentemente, alguma combinação dos mesmos.
Com relação à similaridade de sintaxe com Java, Scala herda muito da sintaxe de Java, como é o caso com Groovy. O Clojure, por outro lado, segue a sintaxe Lisp , que é diferente tanto na aparência quanto na filosofia. No entanto, aprender Scala também é considerado difícil por causa de seus muitos recursos avançados. Este não é o caso do Groovy, apesar de também ser uma linguagem rica em recursos, principalmente porque foi projetada para ser principalmente uma linguagem de script.
Adoção
Rankings de idioma
A partir de 2021, as linguagens baseadas em JVM como Clojure, Groovy, Kotlin, Scala são significativamente menos populares do que a linguagem Java original, que geralmente é classificada nos três primeiros lugares e que também está evoluindo simultaneamente com o tempo.
O Índice de popularidade da linguagem de programação, que rastreia pesquisas por tutoriais de linguagem, classificou Scala em 15º em abril de 2018 com uma pequena tendência de queda e em 17º em janeiro de 2021. Isso torna Scala a 3ª linguagem JVM mais popular depois de Java e Kotlin , classificada em 12ª .
O índice TIOBE de popularidade de linguagem de programação emprega classificações em mecanismos de pesquisa da Internet e contagem de publicações semelhantes para determinar a popularidade da linguagem. Em setembro de 2021, ele mostra Scala em 31º lugar. Nesta classificação, Scala está à frente de Haskell (38º) e Erlang , mas abaixo de Go (14º), Swift (15º) e Perl (19º).
O RedMonk Programming Language Rankings, que estabelece classificações com base no número de projetos GitHub e perguntas feitas no Stack Overflow , classifica o Scala em 14º. Aqui, Scala é colocado dentro de um grupo de segunda linha de línguas-à frente de Go , PowerShell , e Haskell , e atrás Swift , Objective-C , Typescript , e R .
Na edição de 2018 da pesquisa State of Java , que coletou dados de 5160 desenvolvedores sobre vários tópicos relacionados a Java, o Scala ficou em terceiro lugar em termos de uso de linguagens alternativas na JVM. Em relação à edição do ano anterior da pesquisa, o uso do Scala entre as linguagens JVM alternativas caiu de 28,4% para 21,5%, superado pelo Kotlin, que aumentou de 11,4% em 2017 para 28,8% em 2018.
Em 2013, quando o Scala estava na versão 2.10, o ThoughtWorks Technology Radar, que é um relatório semestral baseado na opinião de um grupo de tecnólogos seniores, recomendou a adoção do Scala em sua categoria de linguagens e frameworks. Em julho de 2014, esta avaliação foi tornada mais específica e agora se refere a um “Scala, as partes boas”, que é descrito como “Para usar o Scala com sucesso, você precisa pesquisar o idioma e ter uma opinião muito forte sobre quais partes estão certas para você, criando sua própria definição de Scala, as partes boas. ”.
Empresas
- Em abril de 2009, o Twitter anunciou que havia mudado grande parte de seu back-end de Ruby para Scala e pretendia converter o resto.
- Gilt usa Scala e Play Framework .
- O Foursquare usa Scala e Lift .
- O Coursera usa Scala e Play Framework .
- A Apple Inc. usa Scala em certas equipes, junto com Java e a estrutura Play.
- O site de alto tráfego do jornal The Guardian , guardian.co.uk, anunciou em abril de 2011 que estava mudando de Java para Scala.
- O New York Times revelou em 2014 que seu sistema de gerenciamento de conteúdo interno Blackbeard é construído usando Scala, Akka e Play.
- O jornal Huffington Post começou a empregar Scala como parte de seu sistema de entrega de conteúdo Athena em 2013.
- O banco suíço UBS aprovou o Scala para uso em produção geral.
- O LinkedIn usa o microframework Scalatra para alimentar sua API Signal.
- Meetup usa kit de ferramentas não filtrado para APIs em tempo real.
- Lembre-se de que o Milk usa o kit de ferramentas não filtrado, Scala e Akka para API pública e atualizações em tempo real.
- A Verizon está procurando fazer "uma estrutura de próxima geração" usando Scala.
- A Airbnb desenvolve o software de aprendizado de máquina de código aberto "Aerosolve", escrito em Java e Scala.
- Zalando mudou sua pilha de tecnologia de Java para Scala and Play.
- O SoundCloud usa Scala para seu back-end, empregando tecnologias como Finagle (micro serviços), Scalding e Spark (processamento de dados).
- O Databricks usa Scala para a plataforma Apache Spark Big Data.
- Morgan Stanley usa Scala extensivamente em seus projetos financeiros e relacionados a ativos.
- Existem equipes no Google e na Alphabet Inc. que usam Scala, principalmente devido a aquisições como Firebase e Nest.
- O Walmart Canadá usa Scala para sua plataforma de back-end.
- Duolingo usa Scala para seu módulo de back-end que gera aulas.
- O HMRC usa o Scala para muitas aplicações fiscais do governo do Reino Unido.
- M1 Finance usa Scala para sua plataforma de back-end.
Crítica
Em março de 2015, o ex-VP do grupo de Engenharia de Plataforma do Twitter Raffi Krikorian , afirmou que não teria escolhido o Scala em 2011 devido à sua curva de aprendizado . No mesmo mês, o vice-presidente sênior do LinkedIn, Kevin Scott, declarou sua decisão de "minimizar [sua] dependência do Scala". Em novembro de 2011, o Yammer se afastou do Scala por motivos que incluíam a curva de aprendizado para novos membros da equipe e a incompatibilidade de uma versão do compilador Scala para a próxima.
Veja também
- sbt , uma ferramenta de construção amplamente usada para projetos Scala
- Toque! , uma estrutura de aplicativo da Web de código aberto que suporta Scala
- Akka , um kit de ferramentas de código aberto para a construção de aplicativos simultâneos e distribuídos
- Chisel , uma linguagem de código aberto construída em Scala que é usada para design e geração de hardware.
Referências
Leitura adicional
- Odersky, Martin; Spoon, Lex; Venners, Bill (15 de dezembro de 2019). Programação no Scala: um guia passo a passo abrangente (4ª ed.). Artima Inc . p. 896. ISBN 978-0-9815316-1-8.
- Horstmann, Cay (15 de dezembro de 2016). Scala for the Impacient (2ª ed.). Addison-Wesley Professional . p. 384. ISBN 978-0-134-54056-6.
- Wampler, Dean; Payne, Alex (14 de dezembro de 2014). Escala de Programação: Escalabilidade = Programação Funcional + Objetos (2ª ed.). O'Reilly Media . p. 583. ISBN 978-1-491-94985-6.
- Suereth, Joshua D. (primavera de 2011). Scala em profundidade . Manning Publications . p. 225 . ISBN 978-1-935182-70-2.
- Meredith, Gregory (2011). Padrões de projeto monádicos para a Web (PDF) (1ª ed.). p. 300
- Odersky, Martin; Spoon, Lex; Venners, Bill (10 de dezembro de 2008). Programação em Scala, eBook (1ª ed.). Artima Inc .