O que o operador `#` significa no Scala?

131

Vejo este código neste blog: Programação em nível de tipo no Scala :

// define the abstract types and bounds
trait Recurse {
  type Next <: Recurse
  // this is the recursive function definition
  type X[R <: Recurse] <: Int
}
// implementation
trait RecurseA extends Recurse {
  type Next = RecurseA
  // this is the implementation
  type X[R <: Recurse] = R#X[R#Next]
}
object Recurse {
  // infinite loop
  type C = RecurseA#X[RecurseA]
}

Existe um operador #no código R#X[R#Next]que eu nunca vi. Como é difícil pesquisá-lo (ignorado pelos mecanismos de pesquisa), quem pode me dizer o que isso significa?

Vento livre
fonte
1
o "sinal de libra" às vezes é chamado de "octathrop" (que a pesquisa do Google me levou a esta página).
philwalk
3
Faça esse octothorpe ou octothorp
smparkes
O que há sobre outros operadores como # + e # - (consulte github.com/tpolecat/doobie/blob/series/0.4.x/yax/h2/src/main/… )? Existe uma lista abrangente?
Markus Barthlen

Respostas:

240

Para explicar, primeiro precisamos explicar as classes aninhadas no Scala. Considere este exemplo simples:

class A {
  class B

  def f(b: B) = println("Got my B!")
}

Agora vamos tentar algo com isso:

scala> val a1 = new A
a1: A = A@2fa8ecf4

scala> val a2 = new A
a2: A = A@4bed4c8

scala> a2.f(new a1.B)
<console>:11: error: type mismatch;
 found   : a1.B
 required: a2.B
              a2.f(new a1.B)
                   ^

Quando você declara uma classe dentro de outra classe no Scala, está dizendo que cada instância dessa classe possui uma subclasse. Em outras palavras, não há nenhuma A.Bclasse, mas existem a1.Be a2.Baulas, e eles são diferentes classes, como a mensagem de erro está nos dizendo acima.

Se você não entendeu isso, procure os tipos dependentes do caminho.

Agora, #é possível consultar essas classes aninhadas sem restringi-las a uma instância específica. Em outras palavras, não existe A.B, mas existe A#B, o que significa uma Bclasse aninhada de qualquer instância de A.

Podemos ver isso no trabalho, alterando o código acima:

class A {
  class B

  def f(b: B) = println("Got my B!")
  def g(b: A#B) = println("Got a B.")
}

E experimentando:

scala> val a1 = new A
a1: A = A@1497b7b1

scala> val a2 = new A
a2: A = A@2607c28c

scala> a2.f(new a1.B)
<console>:11: error: type mismatch;
 found   : a1.B
 required: a2.B
              a2.f(new a1.B)
                   ^

scala> a2.g(new a1.B)
Got a B.
Daniel C. Sobral
fonte
Excelente exemplo. Eu aceito totalmente que funciona dessa maneira, mas é difícil entender isso: scala> classOf [A # B] res7: classe [A # B] = classe A $ B scala> classOf [aB] res8: classe [aB] = classe A $ B. o que significa que eles realmente têm o mesmo tipo?
Quíron
2
Seus valores têm a mesma representação de cadeia - e podem até ser iguais. Classé uma representação em tempo de execução de classes Java e é limitada mesmo em Java. Por exemplo, List<String>e List<Integer>tem o mesmo tempo de execução Class. Se Classnão for rico o suficiente para representar os tipos Java , é quase inútil ao representar os tipos Scala . Novamente, a res7: Class[A#B] = class A$Besquerda do sinal de igual é um tipo, à direita do tipo igual se um valor que é a representação do Java Runtime de uma classe.
Daniel C. Sobral
13

É conhecido como projeção de tipo e é usado para acessar os membros do tipo.

scala> trait R {
     |   type A = Int
     | }
defined trait R

scala> val x = null.asInstanceOf[R#A]
x: Int = 0
desaparecido
fonte
6
Esta é uma não resposta. Basicamente, mostra o mesmo código da pergunta, apenas um pouco reduzido. Qual é, por exemplo, a diferença de apontar notação? Onde eu usaria esse # no código real?
Notan3xit
2
@ notan3xit talvez não seja uma resposta para o que você queria perguntar. Mas o que você perguntou é "... o que eu nunca vi. Como é difícil pesquisá-lo (ignorado pelos mecanismos de pesquisa), quem pode me dizer o que isso significa?"
nafg