Aqui estão algumas razões para usar o método deliciosamente simples implicitly
.
Para entender / solucionar problemas de vistas implícitas
Uma visualização implícita pode ser acionada quando o prefixo de uma seleção (considere, por exemplo, the.prefix.selection(args)
não contém um membro selection
aplicável a ele args
(mesmo depois de tentar converter args
com as visualizações implícitas) .Neste caso, o compilador procura por membros implícitos, definidos localmente nos escopos atuais ou anexos, herdados ou importados, que são Funções do tipo the.prefix
para um tipo com selection
métodos implícitos definidos ou equivalentes.
scala> 1.min(2) // Int doesn't have min defined, where did that come from?
res21: Int = 1
scala> implicitly[Int => { def min(i: Int): Any }]
res22: (Int) => AnyRef{def min(i: Int): Any} = <function1>
scala> res22(1) //
res23: AnyRef{def min(i: Int): Int} = 1
scala> .getClass
res24: java.lang.Class[_] = class scala.runtime.RichInt
As vistas implícitas também podem ser acionadas quando uma expressão não está em conformidade com o tipo esperado, como abaixo:
scala> 1: scala.runtime.RichInt
res25: scala.runtime.RichInt = 1
Aqui o compilador procura esta função:
scala> implicitly[Int => scala.runtime.RichInt]
res26: (Int) => scala.runtime.RichInt = <function1>
Acessando um parâmetro implícito introduzido por um limite de contexto
Parâmetros implícitos são indiscutivelmente um recurso mais importante do Scala que as visualizações implícitas. Eles suportam o padrão de classe de tipo. A biblioteca padrão usa isso em alguns lugares - veja scala.Ordering
e como é usado SeqLike#sorted
. Parâmetros implícitos também são usados para transmitir manifestos e CanBuildFrom
instâncias de matriz .
O Scala 2.8 permite uma sintaxe abreviada para parâmetros implícitos, chamados limites de contexto. Resumidamente, um método com um parâmetro de tipo A
que requer um parâmetro implícito do tipo M[A]
:
def foo[A](implicit ma: M[A])
pode ser reescrito como:
def foo[A: M]
Mas qual é o sentido de passar o parâmetro implícito, mas não de nomeá-lo? Como isso pode ser útil ao implementar o método foo
?
Freqüentemente, o parâmetro implícito não precisa ser referido diretamente, ele será encaminhado como um argumento implícito para outro método chamado. Se for necessário, você ainda pode manter a assinatura do método conciso com o Limite de Contexto e chamar implicitly
para materializar o valor:
def foo[A: M] = {
val ma = implicitly[M[A]]
}
Passando um subconjunto de parâmetros implícitos explicitamente
Suponha que você esteja chamando um método que imprima bastante uma pessoa, usando uma abordagem baseada em classe de tipo:
trait Show[T] { def show(t: T): String }
object Show {
implicit def IntShow: Show[Int] = new Show[Int] { def show(i: Int) = i.toString }
implicit def StringShow: Show[String] = new Show[String] { def show(s: String) = s }
def ShoutyStringShow: Show[String] = new Show[String] { def show(s: String) = s.toUpperCase }
}
case class Person(name: String, age: Int)
object Person {
implicit def PersonShow(implicit si: Show[Int], ss: Show[String]): Show[Person] = new Show[Person] {
def show(p: Person) = "Person(name=" + ss.show(p.name) + ", age=" + si.show(p.age) + ")"
}
}
val p = Person("bob", 25)
implicitly[Show[Person]].show(p)
E se quisermos mudar a maneira como o nome é exibido? Podemos chamar explicitamente PersonShow
, passar explicitamente uma alternativa Show[String]
, mas queremos que o compilador passe a Show[Int]
.
Person.PersonShow(si = implicitly, ss = Show.ShoutyStringShow).show(p)