Scala “<-” para compreensão

Eu descobri que Scala sempre tem uma “explicação natural” para qualquer coisa. Sempre algo como “ohh, mas isso é apenas uma function sendo chamada neste e naquele object com este e aquele parâmetro”. De certo modo, nada é realmente magia-compilador como a conhecemos de outras linguagens.

Minha pergunta é sobre o operador <- , conforme usado no código a seguir:

for(i <- 0 to 10) println(i) 

Neste exemplo, posso vê-lo sendo reescrito para algo como:

 0.to(10).foreach((i:Int)=>println(i)) 

mas isso não explica como eu fui levado para a function anônima dentro da function foreach. No ponto em que você escreve, não é um object, e ainda não é uma variável declarada. Então, o que é isso, e como isso está sendo levado para o interior do foreach?

Meu palpite é que eu finalmente descobri algo que é na verdade mágica do compilador

Obrigado pelo seu tempo.

Para esclarecer, minha pergunta é: como o operador <- funciona na primeira linha de código, uma vez que i não é um objeto no qual ele pode ser chamado como uma função.

< - é um símbolo de palavra-chave definido pela linguagem, como é => mas em contraste distinto para -> (que é um símbolo definido). Como faz parte da gramática básica do Scala, ele pode ser usado para criar associações (para o i em seu exemplo), que é algo que não pode ser feito por construções definidas pelo usuário.

Para aumentar a resposta de Dave, aqui está um esquema de tradução para ‘for-comprehensions’ da especificação da linguagem Scala:

Uma compreensão for (enums) yield e avalia a expressão e para cada binding gerada pelas enumerações enums. Uma sequência enumeradora sempre começa com um gerador; isso pode ser seguido por mais geradores, definições de valor ou proteções.

Um gerador p < - e produz ligações de uma expressão e que é correspondido de alguma forma contra o padrão p . Uma definição de valor val p = e liga o nome do valor p (ou vários nomes em um padrão p ) ao resultado da avaliação da expressão e . Um guarda if e contém uma expressão booleana que restringe as ligações enumeradas.

O significado preciso de geradores e guardas é definido por translação para invocações de quatro methods: map , filter , flatMap e foreach . Esses methods podem ser implementados de maneiras diferentes para diferentes tipos de operadoras.

O esquema de tradução é o seguinte. Em um primeiro passo, todo gerador p < - e , onde p não é irrefutável (§8.1) para o tipo de e é substituído por

  p < - e.filter { case p => true; case _ => false } 

Então, as seguintes regras são aplicadas repetidamente até que todas as compreensões sejam eliminadas.

  • Uma compreensão for (p < - e) yield e0 é traduzido para e.map { case p => e0 } .

  • Uma compreensão for (p < - e) e0 é traduzida para e.foreach { case p => e0 } .

  • Uma compreensão for (p < - e; p0 <- e0 . . .) yield e00 , onde. . . é uma sequência (possivelmente vazia) de geradores ou guardas, é traduzida para:
    e.flatMap { case p => for (p0 < - e0 . . .) yield e00 } .

  • Uma compreensão for (p < - e; p0 <- e0 . . .) e00 onde. . . é uma sequência (possivelmente vazia) de geradores ou guardas, é traduzida para:
    e.foreach { case p => for (p0 < - e0 . . .) e00 } .

  • Um gerador p < - e seguido por um guarda if g é traduzido para um único gerador:
    p < - e.filter((x1, . . . , xn) => g )
    onde x1 ,. . . , xn são as variables ​​livres de p .

  • Um gerador p < - e seguido por uma definição de valor val p0 = e0 é traduzido para o seguinte gerador de pares de valores, onde x e x0 são nomes novos:

     val (p, p0) < - for(x@p <- e) yield { val x0@p0 = e0; (x, x0) } 

Neste caso, é realmente um pouco de mágica do compilador. A tradução do for-comprehension para o filtro / mapa / formulário de mapa plano é um pouco especial de desatualização, bem como a conversão das formas especiais de atualização e aplicação de methods.