Scala (linguagem de programação) - Scala (programming language)

Scala
Scala-full-color.svg
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 ; 17 anos atrás ( 20/01/2004 )
Versão estável
3.0.1  Edite isso no Wikidata / 9 de julho de 2021 ; 2 meses atrás ( 9 de julho de 2021 )
Versão de visualização
3.0.0-RC3  Edite isso no Wikidata / 19 de abril de 2021 ; há 4 meses ( 19 de abril de 2021 )
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 .scala-lang .org
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, Booleanvez de int, 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 ) ou var(indica uma variável mutável ).
  • O returnoperador é 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 usa foo.asInstanceOf[Type], ou uma função especializada, como toDoubleou toInt.
  • Em vez de Java import foo.*;, Scala usa import foo._.
  • Função ou método foo()também pode ser chamado como justo foo; método thread.send(signo)também pode ser chamado como justo thread send signo; e o método foo.toString()também pode ser chamado de justo foo 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 de array[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 Java List<String>.
  • Em vez do pseudo-tipo void, Scala tem a classe singleton real Unit(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 objectvez de class. É 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, para class Foocom o objeto companheiro object Foo, sob o capô há uma classe Foo$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 valou var, 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 numsã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 applye updatetêm formas sintáticas curtas. foo()—Onde fooestá um valor (objeto singleton ou instância de classe) —é abreviação de foo.apply(), e foo() = 42é abreviação de foo.update(42). Da mesma forma, foo(42)é abreviação de foo.apply(42)e foo(4) = 2é abreviação de foo.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 os def foo() = 42mé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 em foo.toStringvez de foo.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, o 4 :: 2 :: Nilé o mesmo que Nil.::(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 que breakableera 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 que Vector.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, flatMape filter.

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 ! messagepode 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, inte 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 Inte 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, intvs. 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 yieldpalavra - 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, flatMape filterchamadas. Onde yieldnã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 25nã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 mentionsvez 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:

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 voidem C ou Java, e instruções como whileessa 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 throwoperador 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 Nothingcompatí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 returninstruçõ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 Unitsã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 xnem 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 < 2ou 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 varpalavra - chave e os valores imutáveis ​​são declarados usando a valpalavra - chave. Uma variável declarada com a valpalavra - chave não pode ser reatribuída da mesma forma que uma variável declarada com a finalpalavra - 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 lazypalavra - 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 viewmé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.TailCallsdesde 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 matchoperador é usado para fazer correspondência de padrões no objeto armazenado em list. Cada caseexpressão é tentada separadamente para ver se ela corresponde, e a primeira combinação determina o resultado. Nesse caso, Nilcorresponde apenas ao objeto literal Nil, mas pivot :: tailcorresponde 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 pivotsegurando o topo da lista e outra variável tailsegurando 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 letoperador 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 consem Lisp e Scheme) e :::(que anexa duas listas, semelhante a appendem 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 matchoperador é uma função parcial , que consiste em uma série de caseexpressõ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 tryinstruçã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 matchsobre 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 superoperador 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 drawprimeiro 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:

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 Intcom métodos isEvene 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 MyExtensionstraz a conversão implícita para a classe de extensão IntPredicatesno 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.actorOfsubstituindo o receivemé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
  • scala.Enumeration
  • A licença Scala foi alterada para a licença BSD revisada
1.1.1 23 de março de 2004
  • Suporte para classes internas estáticas Java
  • Melhorias de classe de biblioteca para Iterable, Array, xml.Elem, Buffer
1.2.0 9 de junho de 2004
  • Visualizações
  • Literais XML (a "ser descartado em um futuro próximo, para ser substituído pela interpolação de string XML")
1.3.0 16 de setembro de 2004
  • Suporte para Microsoft .NET
  • Método de fechamento
  • A sintaxe de tipo para métodos sem parâmetros mudou de [] Tpara=> T
1.4.0 20 de junho de 2005
  • Atributos
  • matchmatchmétodo de substituição de palavra-chave
  • Suporte experimental para tipos de tempo de execução
2.0 12 de março de 2006
  • Compilador totalmente reescrito em Scala
  • Suporte experimental para genéricos Java
  • implicite requirespalavras - chave
  • match palavra-chave permitida apenas infixo
  • withconectivo só é permitido após uma extendscláusula
  • Novas linhas podem ser usadas como separadores de instrução no lugar de ponto-e-vírgula
  • Padrões de correspondência de expressão regular restritos apenas a padrões de sequência
  • For-compreensões admitem definições de valores e padrões
  • Os parâmetros de classe podem ser prefixados por val ou var
  • A visibilidade privada tem qualificadores
2.1.0 17 de março de 2006
  • Ferramenta sbaz integrada na distribuição Scala
  • matchmatchmétodo de substituição de palavra-chave
  • Suporte experimental para tipos de tempo de execução
2.1.8 23 de agosto de 2006
  • A visibilidade protegida tem qualificadores
  • Membros privados de uma classe podem ser referenciados a partir do módulo complementar da classe e vice-versa
  • Pesquisa implícita generalizada
  • Correspondência de padrão digitado apertada para tipos singleton
2.3.0 23 de novembro de 2006
  • As funções que retornam Unitnão precisam declarar explicitamente um tipo de retorno
  • Variáveis ​​de tipo e tipos são distinguidos na correspondência de padrões
  • Alle AllRefrenomeado para NothingeNull
2.4.0 9 de março de 2007
  • privatee os protectedmodificadores aceitam um [this]qualificador
  • Tuplas podem ser escritas com colchetes
  • O construtor primário de uma classe agora pode ser marcado como privado ou protegido
  • Atributos alterados para anotações com nova sintaxe
  • Aliases próprios
  • Os operadores podem ser combinados com atribuição
2.5.0 2 de maio de 2007
  • Parâmetros de tipo e membros de tipo abstrato também podem abstrair sobre construtores de tipo
  • Os campos de um objeto podem ser inicializados antes que os construtores pais sejam chamados
  • Mudança de sintaxe para compreensões
  • Funções anônimas implícitas (com sublinhados para parâmetros)
  • Correspondência de padrões de funções anônimas estendidas para oferecer suporte a qualquer arte
2.6.0 27 de julho de 2007
  • Tipos existenciais
  • Valores preguiçosos
  • Tipos estruturais
2.7.0 7 de fevereiro de 2008
  • Tipos genéricos Java suportados por padrão
  • Funcionalidade de classes de caso estendida
2.8.0 14 de julho de 2010
  • Revisão da estrutura comum, uniforme e abrangente para tipos de coleção.
  • Especialização de tipo
  • Argumentos nomeados e padrão
  • Objetos de pacote
  • Anotações aprimoradas
2.9.0 12 de maio de 2011
  • Coleções paralelas
  • AppTraço seguro de thread substitui Applicationtraço
  • DelayedInit traço
  • Melhorias de interoperabilidade Java
2,10 4 de janeiro de 2013
  • Classes de valor
  • Classes implícitas
  • Interpolação de string
  • Futuros e promessas
  • Dynamic e applyDynamic
  • Tipos de métodos dependentes:
    • def identity(x: AnyRef): x.type = x // the return type says we return exactly what we got
  • Novo emissor de bytecode baseado em ASM:
    • Pode direcionar JDK 1.5, 1.6 e 1.7
    • Emite 1,6 bytecode por padrão
    • O back-end 1.5 antigo está obsoleto
  • Um novo combinador de padrões: reescrito do zero para gerar um código mais robusto (sem explosão exponencial)
    • geração de código e análises agora são independentes (a última pode ser desligada com -Xno-patmat-analysis)
  • Melhorias no Scaladoc
    • Implícitos (sinalizador -implicits)
    • Diagramas (sinalizador -diagrams, requer graphviz)
    • Grupos (-grupos)
  • Recursos de linguagem modularizada
  • Coleções paralelas agora são configuráveis ​​com pools de threads personalizados
  • Atores Akka agora fazem parte da distribuição
    • scala.actors foram descontinuados e a implementação akka agora está incluída na distribuição.
  • Melhorias de desempenho
    • Inliner mais rápido
    • A soma do intervalo # agora é O (1)
  • Atualização da biblioteca ForkJoin
  • Correções em TreeSet / TreeMap imutável
  • Melhorias para funções parciais
  • Adição de ??? e NotImplementedError
  • Adição de classes do tipo IsTraversableOnce + IsTraversableLike para métodos de extensão
  • Suspensão de uso e limpeza
  • Suspensão de uso da sintaxe literal de ponto flutuante e octal
  • Scala.dbc removido

Recursos experimentais

  • Reflexão Scala
  • Macros
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
  • Melhorias no desempenho da coleção
  • Melhorias de desempenho do compilador
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
  • Java 8 necessário
  • Bytecode Java 8 gerado
  • Suporte à linguagem Java 8 SAM ( interface funcional )
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
  • Primeiro lançamento do Scala 2.12 com a licença alterada para Apache v2.0
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
  • Biblioteca de coleções padrão redesenhada
  • Tipos literais
  • Unificação de tipo parcial
  • Implícitos por nome
  • Otimizações de compilador
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