Programação, Scala

Slick 1.0.0 com Scala 2.11 / Bug de “variance” do Scala 2.11.*

Estamos trabalhando em um projeto que ainda utiliza Slick 1.0.0, porém migramos para Scala 2.11 e Play Framework 2.3.4. Se sua equipe já passou por isso, sabe que não existem versões oficiais do Slick após Maio/2013. A solução é simples, basta baixar o fonte do Slick 1.0.0 e mandar brasa. Certo? Infelizmente, errado.

A primeira resposta que alguém lhe dará é “migre para o Slick 2.0”. Sim, claro. Se tivéssemos 2500 homens-hora para migrar centenas de tabelas e código de acesso aos dados nas classes da camada de acesso aos dados.

Decidimos não migrar para o Slick 2.0. Aliás, na minha opinião, o Slick e o Scala tem andado, perigosamente, na mesma linha do Python 3.0 : novas versões não precisam destruir tudo que veio antes. A migração de Python 2 para 3 poderia ter sido mais veloz caso a compatibilidade retroativa tivesse sido mantida. Podem, também, ser introduzidas camadas de compatibilidade para permitir que a atualização do código ocorra de forma iterativa e gradual. Mas não foi isso que ocorreu com Python e tampouco com a evolução do Slick de 1.x para 2.x.

Enfim, a migração para Scala 2.11 é definitiva para nós, bem como o Play 2.3.4. Então a missão é compilar o Slick usando SBT 0.13.5 e todo o toolset dessas novas versões da linguagem e framework.

O primeiro bug que ocorre é na mudança da API de macros.

[error] /root/scala/slick-1.0.0/src/main/scala/scala/slick/direct/SlickBackend.scala:125: value asFreeTerm is not a member of reflect.runtime.universe.Symbol
[error] case ident:Ident => ident.symbol.asFreeTerm.value

Para corrigir, encontramos essa dica nas mudanças de Scala 2.10 -> 2.11:

27) Introduction of Universe.internal and Context.internal. The following 51 APIs available in Scala 2.10.x have been moved into the internal submodule of the reflection cake. There are two ways of fixing these source incompatibilities. The easy one is writing import compat._ after import scala.reflect.runtime.universe._ or import c.universe._. The hard one is the easy one + applying all migration suggestions provided by deprecating warnings on methods imported from compat.

A boa notícia é que a alteração é documentada, portanto prevista. E que há uma solução fácil: adicionar import compat._ logo após import scala.reflect.runtime.universe._

Os desenvolvedores da biblioteca padrão realmente pensaram nos mantenedores de código! Resolvido o problema, seguimos adiante com novo sbt compile e encontramos novo entrave:

[error] /root/scala/slick-1.0.0/src/main/scala/scala/slick/lifted/Shape.scala:22: contravariant type Mixed_ occurs in invariant position in type Mixed_ of type Mixed
[error] type Mixed = Mixed_

O problema aqui está documentado no SI-6566:

Bodies of non-private type aliases must be invariant

Ou seja, o lado direito da declaração type Mixed = Mixed_ deve ser não variante, nesse caso não é. Vamos dar um tiro no escuro e pedir para o compilador ignorar a variância de Mixed_ pois, como Slick 1.0.0 foi amplamente testado e usado, sabemos que nesse caso não deve haver grande risco de permitir que continue assim. Vejamos.

Primeiro um import obrigatório:
import scala.annotation.unchecked

E a anotação é adicionada.
type Mixed = Mixed_
se torna
type Mixed = (Mixed_ @uncheckedVariance)

E voilà.

[success] Total time: 40 s, completed Sep 4, 2014 10:39:46 AM

Slick 1.0.0 compila com sucesso no Scala 2.11.1 + SBT 0.13.5.

Conclusão

Trabalhar com Play Framework e Scala dobrou nossa produtividade. Apesar do aprendizado da linguagem Scala exigir uma certa curva inicial, depois que se pega o jeito o workflow se torna muito simples. É preciso também tornar-se um bom leitor de scaladocs : 99% dos problemas em Scala se resolvem lendo código fonte e não em documentação “mastigada”.

Na verdade essa é uma característica da comunidade Scala que os iniciantes acham complicado de vencer: realmente falta documentação, parte-se do pressuposto que as pessoas vão ler código fonte e documentação oficial.

No caso aqui resolvemos o problema lendo especificações oficiais, os Scala Issues(SI-6566), o documento changes que documenta as mudanças de versões Scala, e assim por diante. Espero que as dicas ajudem outros colegas aficionados por Scala.

Standard