Scala: Implicit Converters

No post anterior entre outras coisas, comentei de implicit arguments, agora vou comentar um pouco de implicit converters.

Implicit Converters

Outra funcionalidade para os implicits são os conversores implicitos. Essa funcionalidade permite adicionarmos características super dinamicas mas sem deixar que fiquemos com a sensação de não sabermos de onde vem as coisas. O que o compilador do Scala faz é olhar se estamos tentando usar um objeto de um determinado tipo como se fosse de um outro tipo, e então procura uma função que converta um objeto do primeiro tipo para o último.

Por exemplo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Vaca {
    def mugir() = "muuuuuuu"
}
//...
class Cachorro {
    def latir() = "au!"
}
 
//...
 
var mimosa = new Vaca
 
def passearComCachorro(toto: Cachorro) = {
    toto.latir()
}
 
passearComCachorro(mimosa) // isso vai dar erro?!
 
//a não ser que eu tenha algo assim
class Vacachorro(vaca: Vaca) extends Cachorro {
    override def latir() = "Antes eu fazia " + vaca.mugir() + ", mas agora eu sei latir!"
}
 
//e esse método converte
implicit def funcaoQueConverteVacaEmCachorro(vaca: Vaca) = new Vacachorro(vaca)

Esse código parece bem esquisito mas funciona. Eu dei um exemplo bem esdrúxulo apenas para mostrar a possibilidade que o Scala dá. Obviamente os conversores geralmente são usados para converter um tipo numérico em outro, ou coisas do genero, mas dei um exemplo pouco convencional propositalmente.
Na linha 17 o compilador do Scala procura por uma função que receba uma Vaca e devolva um Cachorro, porque é o tipo de conversão que ele percebe que precisa acontecer. Entao o compilador faz algo assim:

passearComCachorro(funcaoQueConverteVacaEmCachorro(mimosa))

Agora caso o compilador não encontrasse nenhuma, ou mais de uma função desse tipo, ele daria erro de compilação.
Mas fora converter ao chamar uma função, o Scala permite também fazer isso:

mimosa.latir()

Que em tempo de compilação o Scala vai transformar em

funcaoQueConverteVacaEmCachorro(mimosa).latir()

Novamente gostaria de frisar que esse exemplo é mais didático do que funcional. Na prática poderíamos criar um conversor assim

1
2
3
implicit def conversorQualquer(objeto: Any) = new {
        def latir() = "Um objeto qualquer agora passou a latir"
}

Ou então poderíamos ter esses conversores em alguma outra classe e usar “static imports” para importar esses conversores para nosso código.

Lembrando sempre que se se não houver, ou haver mais de um conversor compatível, o compilador vai dar erro de compilação.

Conversores implicitos dão a mesma facilidade de “open classes” com a vantagem que essa conversão só terá efeito no trecho de código onde a função de conversão está visível. Não acontece de adicionarmos uma função em um objeto num canto do sistema e a mesma estar visível em outro, o que é uma desvantagem em potencial das open classes.

Esses conversores são usados muitas vezes pela pelo próprio Scala. Vejam por exemplo a documentação da classe Int onde é dito que existe um conversor implícito que converte uma instancia de Int para RichInt.

Num próximo post vou comentar sobre mixins classes com Scala.