O que é o identificador Scala "implicitamente"?

169

Eu vi uma função nomeada implicitlyusada nos exemplos do Scala. O que é e como é usado?

Exemplo aqui :

scala> sealed trait Foo[T] { def apply(list : List[T]) : Unit }; object Foo {
     |                         implicit def stringImpl = new Foo[String] {
     |                             def apply(list : List[String]) = println("String")
     |                         }
     |                         implicit def intImpl = new Foo[Int] {
     |                             def apply(list : List[Int]) =  println("Int")
     |                         }
     |                     } ; def foo[A : Foo](x : List[A]) = implicitly[Foo[A]].apply(x)
defined trait Foo
defined module Foo
foo: [A](x: List[A])(implicit evidence$1: Foo[A])Unit

scala> foo(1)
<console>:8: error: type mismatch;
 found   : Int(1)
 required: List[?]
       foo(1)
           ^
scala> foo(List(1,2,3))
Int
scala> foo(List("a","b","c"))
String
scala> foo(List(1.0))
<console>:8: error: could not find implicit value for evidence parameter of type
 Foo[Double]
       foo(List(1.0))
          ^

Observe que temos que escrever, implicitly[Foo[A]].apply(x)pois o compilador pensa que isso implicitly[Foo[A]](x)significa que chamamos implicitlycom parâmetros.

Veja também Como investigar objetos / tipos / etc. do Scala REPL? e Onde Scala procura implícitos?

oluies
fonte

Respostas:

206

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 selectionaplicável a ele args(mesmo depois de tentar converter argscom 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.prefixpara um tipo com selectionmé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.Orderinge como é usado SeqLike#sorted. Parâmetros implícitos também são usados ​​para transmitir manifestos e CanBuildFrominstâ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 Aque 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 implicitlypara 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)
retrônimo
fonte
2
scala> 1.min (2) res0: Int = 1 No Scala 2.10.3, recebo um erro: scala> implicitamente [Int => {def min (i: Int): Qualquer}] <console>: 8: erro: Nenhuma visualização implícita disponível em Int => AnyRef {def min (i: Int): Qualquer}. implicitamente [Int => {def min (i: Int): Qualquer}]
jhegedus
Esta resposta seria atualizada para a versão mais recente.
emeth
1
implicitamente [Int => AnyVal {def min (i: Int): Int}] funcionará. Deve ser corrigido na resposta.
Malkaviano
212

Implicitlyestá disponível no Scala 2.8 e é definido no Predef como:

def implicitly[T](implicit e: T): T = e

É comumente usado para verificar se um valor implícito do tipo Testá disponível e retorná-lo, se for o caso.

Exemplo simples da apresentação do retronym :

scala> implicit val a = "test" // define an implicit value of type String
a: java.lang.String = test
scala> val b = implicitly[String] // search for an implicit value of type String and assign it to b
b: String = test
scala> val c = implicitly[Int] // search for an implicit value of type Int and assign it to c
<console>:6: error: could not find implicit value for parameter e: Int
       val c = implicitly[Int]
                         ^
oluies
fonte
6
O método não verifica exatamente; parece causar um erro de compilação se não houver um valor implícito disponível e, se houver, parece recuperá-lo. Você pode fornecer um pouco mais de contexto sobre por que eu gostaria de usar isso?
precisa saber é o seguinte
17
implicitly[Ordering[(Int, String)]].compare( (1, "b"), (1, "a") ), especialmente para recuperar um parâmetro implícito introduzido por um limite de contexto:def foo[A: Ordering](a1: A, a2: A) = implicitly[Ordering[A]].compare(a1, a2)
retronym
1
Para ver a discussão do retronym no link do vídeo acima, pule para o ponto 13:50.
chaotic3quilibrium
-2

Uma resposta "ensinar você a pescar" é usar o índice alfabético de membros atualmente disponível nas noites de Scaladoc . As letras (e os #nomes não alfabéticos) na parte superior do painel pacote / classe são links para o índice de nomes de membros que começam com essa letra (em todas as classes). Se você escolher I, por exemplo, você encontrará a implicitlyentrada com uma ocorrência, em Predef, que pode ser acessada no link lá.

Randall Schulz
fonte
46
Obviamente, esses escaladocs não dizem nada sobre implicitamente, de modo que dificilmente conta como documentação. Como alguém descobriria o que esse método faz somente com esses documentos? Sinto-me rotineiramente decepcionado com a documentação do Scala. O comportamento de métodos como implicitamente está longe de ser óbvio, e a documentação neles é apenas melhor do que inexistente. Graças a Deus pelo estouro de pilha. / end rant
Jeff
4
A assinatura de tipo documenta essa bem.
retronym
21
implicitparece ser uma característica importante da linguagem em Scala e definitivamente merece uma explicação adequada. Pensar que os documentos que detalham apenas uma contagem de assinaturas de tipo parece mais com satisfação pessoal do que uma resposta genuína. Veja as perguntas específicas feitas pelo OP - o que é e como é usado? Nem respondeu por isso nem nos documentos noturnos aos quais você nem fornece um link real. scala-lang.org/files/archive/nightly/docs/library/… Isso não ensina nada. Veja Niklaus Wirth ou Turbo Pascal para exemplos de documentos originais. -1
Thomas W
3
implicite implicitlysão relacionados, mas bem distintos. A implicitpalavra-chave faz parte do idioma. implicitlyé definido no código Scala simples na biblioteca padrão. Como os documentos on-line incluem links de origem, acredito que ainda é melhor encaminhar os questionadores para esses documentos e a fonte vinculada.
Randall Schulz