No Scala, podemos usar pelo menos dois métodos para adaptar os tipos existentes ou novos. Suponha que queremos expressar que algo pode ser quantificado usando um Int
. Podemos definir o seguinte traço.
Conversão implícita
trait Quantifiable{ def quantify: Int }
E então podemos usar conversões implícitas para quantificar, por exemplo, Strings e Lists.
implicit def string2quant(s: String) = new Quantifiable{
def quantify = s.size
}
implicit def list2quantifiable[A](l: List[A]) = new Quantifiable{
val quantify = l.size
}
Depois de importá-los, podemos chamar o método quantify
em strings e listas. Observe que a lista quantificável armazena seu comprimento, portanto, evita o percurso caro da lista em chamadas subsequentes para quantify
.
Classes de tipo
Uma alternativa é definir uma "testemunha" Quantified[A]
que afirma que algum tipo A
pode ser quantificado.
trait Quantified[A] { def quantify(a: A): Int }
Em seguida, fornecemos instâncias desse tipo de classe para String
e em List
algum lugar.
implicit val stringQuantifiable = new Quantified[String] {
def quantify(s: String) = s.size
}
E se escrevermos um método que precisa quantificar seus argumentos, escrevemos:
def sumQuantities[A](as: List[A])(implicit ev: Quantified[A]) =
as.map(ev.quantify).sum
Ou usando a sintaxe associada ao contexto:
def sumQuantities[A: Quantified](as: List[A]) =
as.map(implicitly[Quantified[A]].quantify).sum
Mas quando usar qual método?
Agora vem a pergunta. Como posso decidir entre esses dois conceitos?
O que percebi até agora.
classes de tipo
- classes de tipo permitem a boa sintaxe ligada ao contexto
- com classes de tipo, não crio um novo objeto wrapper em cada uso
- a sintaxe ligada ao contexto não funciona mais se a classe de tipo tiver vários parâmetros de tipo; imagine que eu queira quantificar as coisas não apenas com números inteiros, mas com valores de algum tipo geral
T
. Eu gostaria de criar uma classe de tipoQuantified[A,T]
conversão implícita
- como eu crio um novo objeto, posso armazenar valores em cache ou calcular uma representação melhor; mas devo evitar isso, visto que pode acontecer várias vezes e uma conversão explícita provavelmente seria invocada apenas uma vez?
O que espero de uma resposta
Apresente um (ou mais) casos de uso em que a diferença entre os dois conceitos seja importante e explique por que eu preferiria um em vez do outro. Também explicar a essência dos dois conceitos e sua relação entre si seria bom, mesmo sem exemplo.
size
de uma lista em um valor e diz que isso evita o percurso caro da lista em chamadas subsequentes para quantificar, mas em cada chamada para quantify
o list2quantifiable
é acionado tudo de novo, reinstanciando Quantifiable
e recalculando a quantify
propriedade. O que estou dizendo é que, na verdade, não há como armazenar em cache os resultados com conversões implícitas.