O que é um Manifesto em Scala e quando você precisa?

132

Desde o Scala 2.7.2, existe algo chamado Manifestque é uma solução alternativa para o apagamento de tipo do Java. Mas como Manifestfunciona exatamente e por que / quando você precisa usá-lo?

O blog pós Manifests: reificado Tipos de Jorge Ortiz explica algumas delas, mas não explica como usá-lo em conjunto com limites de contexto .

Além disso, qual é ClassManifesta diferença Manifest?

Eu tenho algum código (parte de um programa maior, não pode incluí-lo facilmente aqui) que possui alguns avisos com relação ao apagamento de tipo; Eu suspeito que posso resolver isso usando manifestos, mas não sei exatamente como.

Jesper
fonte
2
Houve uma discussão na lista de discussão sobre o / diferença ClassManifest manifesto, consulte scala-programming-language.1934581.n4.nabble.com/...
Arjan Blokzijl

Respostas:

197

O compilador conhece mais informações sobre tipos do que o tempo de execução da JVM pode representar facilmente. Um manifesto é uma maneira do compilador enviar uma mensagem interdimensional para o código em tempo de execução sobre as informações de tipo que foram perdidas.

É semelhante à maneira como os Kleptonianos deixaram mensagens codificadas nos registros fósseis e no DNA "lixo" dos seres humanos. Devido às limitações dos campos de velocidade da luz e ressonância gravitacional, eles não conseguem se comunicar diretamente. Mas, se você sabe como sintonizar o sinal deles, pode se beneficiar de maneiras que não pode imaginar, ao decidir o que comer no almoço ou qual número de loteria jogar.

Não está claro se um manifesto beneficiaria os erros que você está vendo sem saber mais detalhes.

Um uso comum dos manifestos é fazer com que seu código se comporte de maneira diferente com base no tipo estático de uma coleção. Por exemplo, e se você quisesse tratar uma [String] de lista diferente de outros tipos de uma lista:

 def foo[T](x: List[T])(implicit m: Manifest[T]) = {
    if (m <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }

  foo(List("one", "two")) // Hey, this list is full of strings
  foo(List(1, 2)) // Non-stringy list
  foo(List("one", 2)) // Non-stringy list

Uma solução baseada em reflexão para isso provavelmente envolveria a inspeção de cada elemento da lista.

Um limite de contexto parece mais adequado ao uso de classes de tipo no scala e é bem explicado aqui por Debasish Ghosh: http://debasishg.blogspot.com/2010/06/scala-implicits-type-classes-here-i.html

Os limites de contexto também podem tornar as assinaturas do método mais legíveis. Por exemplo, a função acima pode ser reescrita usando limites de contexto da seguinte maneira:

  def foo[T: Manifest](x: List[T]) = {
    if (manifest[T] <:< manifest[String])
      println("Hey, this list is full of strings")
    else
      println("Non-stringy list")
  }
Mitch Blevins
fonte
25

Não é uma resposta completa, mas com relação à diferença entre Manifeste ClassManifest, você pode encontrar um exemplo no documento Scala 2.8Array :

A única questão restante é como implementar a criação genérica de matrizes. Ao contrário do Java, o Scala permite a criação de uma instância nova, Array[T]onde Té um parâmetro de tipo. Como isso pode ser implementado, considerando o fato de que não existe uma representação uniforme de array em Java?

A única maneira de fazer isso é exigir informações adicionais de tempo de execução que descrevam o tipo T. O Scala 2.8 possui um novo mecanismo para isso, chamado de manifesto . Um objeto do tipo Manifest[T]fornece informações completas sobre o tipo T.
Manifestvalores são normalmente passados ​​em parâmetros implícitos; e o compilador sabe como construí-los para tipos estaticamente conhecidos T.

Existe também uma forma mais fraca chamada ClassManifestque pode ser construída a partir do conhecimento apenas da classe de nível superior de um tipo, sem necessariamente conhecer todos os seus tipos de argumento .
É esse tipo de informação de tempo de execução que é necessária para a criação da matriz.

Exemplo:

É necessário fornecer essas informações passando a ClassManifest[T]para o método como um parâmetro implícito:

def  tabulate[T](len:Int,  f:Int=>T)(implicit m:ClassManifest[T]) =  { 
  val  xs  =  new  Array[T](len) 
  for   (i  <- 0  until   len)  xs(i)   = f(i) 
  xs 
} 

Como um formato abreviado, um contexto bound1 pode ser usado no parâmetro type T,

(Veja esta pergunta do SO para ilustração )

, dando:

def  tabulate[T:    ClassManifest](len:Int,  f:Int=>T)  =  { 
  val  xs  =  new  Array[T](len) 
  for   (i  <- 0  until   len)  xs(i)   = f(i) 
  xs 
} 

Ao chamar tabular em um tipo como Int, ou String, ou List[T], o compilador Scala pode criar um manifesto de classe para passar como argumento implícito a tabular.

VonC
fonte
25

Um Manifest foi planejado para reificar tipos genéricos que são apagados para serem executados na JVM (que não suporta genéricos). No entanto, eles tinham alguns problemas sérios: eram muito simplistas e não podiam dar suporte total ao sistema de tipos da Scala. Eles foram descontinuados no Scala 2.10 e são substituídos por TypeTags (que são essencialmente o que o próprio compilador Scala usa para representar tipos e, portanto, oferece suporte total aos tipos Scala). Para mais detalhes sobre a diferença, consulte:

Em outras palavras

quando você precisa?

Antes de 04-01-2013, quando o Scala 2.10 foi lançado .

Caracol mecânico
fonte
Ainda não foi preterido (mas será), pois o Scala reflexão ainda é experimental na versão 2.10.
22413 Keros
Antes de 04-01-2013, ou se você estiver usando uma API que depende dela.
David Moles
1

Vamos também chck manifestem scalasources ( Manifest.scala), vemos:

Manifest.scala:
def manifest[T](implicit m: Manifest[T])           = m

Portanto, com relação ao seguinte código de exemplo:

def foo[A](somelist: List[A])(implicit m: Manifest[A]): String = {
  if (m <:< manifest[String]) {
    "its a string"
  } else {
    "its not a string"
  }
}

podemos ver que a manifest functionpesquisa de um implícito m: Manifest[T]que satisfaz o que type parametervocê fornece em nosso código de exemplo era manifest[String]. Então, quando você chama algo como:

if (m <:< manifest[String]) {

você está verificando se a corrente implicit mque você definiu em sua função é do tipo manifest[String]e, como manifesté uma função do tipo manifest[T], procuraria por um específico manifest[String]e descobriria se existe um implícito.

Tomer Ben David
fonte