Scalando, parte 2

No post anterior a gente viu algumas coisas de Scala de uma forma mais solta, para irmos acostumando com a inguagem. A partir deste post vou tentar mostrar algumas coisas mais específicas.

Functions

Em Scala, uma função é “first-class value”, isso que dizer que assim como qualquer outro valor elas podem ser passadas como parametro para outras funções e serem valores de retorno de funções.

object Main extends Application {
    def percorreLista[T](lista:List[T], funcao:(T) => Unit)= {
    for (elemento <- lista) {
      funcao(elemento)
    }
  }
 
  def imprimeElemento[T](elemento:T) = {
    println(elemento)
  }
 
  percorreLista(List(2,3,4,5), imprimeElemento)
}

Aqui definimos uma função chamada percorreLista que faz o que o nome diz e para cada elemento ela invoca uma outra função passada como parametro, no caso a função imprimeElemento. Reparem na linha 2 a definição dos tipos dos parametros. O primeiro é uma lista do tipo T, nada demais. O segunda parametro é um pouco mais interessante. Definimos uma variável chamada funcao que é do tipo “função void que recebe T como parametro”. Então na linha 12 passamos a função imprimeElemento como parametro para a função percorreLista. A saída desse trecho de código é a seguinte

2
3
4
5

Da mesma forma que passamos uma função como parametro, poderíamos retornar uma também. Mas para não alongar muito isso fica como tarefinha :D

O caracter _

Uma das coisas que eu mais estranhei nos códigos feitos com Scala, foi a constante aparição de um “_”, geralmente sendo passado como argumentos de funções. Isso causou mais estranheza pois em Java quase não temos esse caracter. Mas vamos a mais um exemplo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
object Main extends Application {
 
    def escreveString(s:String) = {
    println("Escrevendo->"+s)
  }
 
  def somatorio(i:Int*) = {
    "Soma=" + i.reduceLeft((a: Int, b: Int) => b + a) // executa mas nao faz efeito
    "Soma=" + i.reduceLeft((a,b) => a + b) //executa mas nao faz efeito
    "Soma=" + i.reduceLeft(_+_) //executa mas nao faz efeito
    "Soma=" + i.foldLeft(0)(_+_) //executa mas nao faz efeito
    "Soma=" + (0 /: i)(_+_) // executa e retorna
  }
 
  escreveString(somatorio(1,4,6))
}

Saída

Escrevendo->Soma=11

Como sempre eu já apriveitei o mesmo exemplo para mostrar várias coisas. Só vou falar da função domatorio, já que o resto só usa o que nós já vimos antes. A começar pelo parametro, é assim que definimos varargs em Scala. Então temos uma função que recebe uma lista de Int de tamanho variável. Na prática i é do tipo Seq[Int]. update: A partir varsão 2.8 varargs são do tipo scala.collection.mutable.WrappedArray, nesse caso mais especificamente do tipo scala.collection.mutable.WrappedArray$ofInt.

No corpo dessa função eu coloquei de formas diferentes a mesma coisa, porém parti do mais legível para o mais ilegível. As funções reduceLeft e foldLeft trabalham de formas diferentes, mas nesse caso o resultado é o mesmo. Olhando o exemplo da documentação fica fácil entender como elas funcionam.

Como eu já disse antes, o retorno de uma função é sua última instrução. Nesse exemplo da função somatório, apenas a linha 12 retorna, as demais efetuam a mesma operação mas o valor é perdido por ninguem usar. A idéia é mostrar as formas diferentes de fazer a mesma coisa, e assim facilitar o entendimento do uso do caracter “_”.

Da linha 8 até a linha 10 usamos a função reduceLeft, e de cima para baixo eu fui reduzindo a especifidade dos parametros. Na linha 8 eu deixei totamente especificado e inverti o ordem ao somar a e b. Não que isso faça diferença no resultado, mas só para exemplificar que a vantagem de se ter as duas variáveis explicitas é poder saber “quem é quem”. Na linha seguinte eu não informei mais o tipo das variáveis a e b pois o Scala consegue perceber isso pra mim, mas continuo podendo brincar com a ordem os parametros pois ainda sei quem é quem.

A partir da linha 10 eu comecei a usar o caracter “_”. Veja que da linha 9 para a 10, olhando o código nós conseguimos perceber que não é tão útil ter as duas variáveis ali pois simplesmente repassamos elas para a expressao a + b. Olhando dessa forma a sintexe da linha 10 parece fazer mais sentido, pois nessa linha nós dizemos que tanto faz quais são as variáveis, eu só quero somá-las. O problema de usar isso é que a legibilidade do código começa a piorar um pouco.

Agora vamos ver as linhas 11 e 12. Olhando a documentação da função foldLeft vemos que ela recebe mais um parametro que o ponto de partida. Como estamos trabalhando com uma soma, passamos o 0, se fosse uma ultiplicação passaríamos 1, pois queremos que esse parametro não influencie no resultado. Mas apesar de mudarmos de função a sintaxe usada las linhas 10 e 11 são equivalentes. Agora na linha 12 bagunçamos de vez e usamos /:, um alias para a função foldLeft. Novamente o problema do código dessa linha é que fica muito ilegível. Olhando para ela, a sintaxe das linhas 10 e 11 até parece mais simpática :D

Múltiplas listas de argumento

Em Scala uma função, diferente do que acontece no Java, pode ter diversas listas de argumentos

object Main extends Application {
    def imprimeStrings(s:String)(s2:String) = {
        println(s2 + "-->" + s)
    }
    imprimeStrings("A", "B")

Saída

B-->A

Pode parecer meio estranho, mas essa sintaxe permite que façamos algo interessante com esse último parametro. A seguir continuamos o código de cima.

    def funcaoQueDevolveString(i:Int) = {
        "Número:" + i
    }
    imprimeStrings("A") {
        funcaoQueDevolveString(5)
    }
}

Saída

A-->Número:5

A funcaoQueDevolveString é só para ilustrar que dentro do bloco de código da chamada da função imprimeStrings eu posso fazer qualquer coisa, desde que o retorno desse corpo seja do tipo do parametro que ele substitui.

Implicit parameters

Outra aplicação para mais de uma lista de argumentos é quando queremos ter uma lista de argumentos implicita. Vamos ao exemplo.

object Main2 extends Application {
    implicit var user = "Gilliard"
 
    def executaOperacao(f:()=>Unit)(implicit usuario:String) = {
        println(usuario + " vai executar uma operação")
        f()
        println(usuario + " executou uma operação")
    }
 
    def operacao() = println("Executando operação")
 
    executaOperacao(operacao)
}

Saída

Gilliard vai executar uma operação
Executando operação
Gilliard executou uma operação

Como isso funciona? Definimos uma função executaOperacao que recebe como primeiro parametro uma função. Até aqui nada de novo. Porém definimos uma segunda lista de argumentos com implicit. A palavra reservada implicit deve ser aplicada no início de uma lista de argumentos, e afeta todos os argumentos dessa lista. Além disso essa lista de argumentos deve ser a última. Então caso eu queira ter argumentos “normais” e argumentos implicitos, eu precisaria ter duas listas de argumentos como no exemplo.

Quando o compilador do Scala encontra uma função com parametros implicitos, ele busca por objetos implicitos que sejam compatíveis com esses parametros. Caso encontre, como no nosso exemplo, tudo bem, agora se não houver nenhum objeto implicito compatível teremos um erro de compilação. Mas isso é bem flexível, se eu não tivesse um objeto implícito e quisesse invocar uma função com parametros implícitos é só eu passar explicitamente esses parametros. Um parametro implícito se torna opcional, mas não é proibido colocar um valor explícito. Agora se mais de um valor explícito compatível estiver disponível o compilador não vai tentar adivinhar qual usar, vai dar erro de compilação.

No próximo post vou falar de implicit converters, que na minha opinião é uma das coisas mais bacanas e simples de usar do Scala. Aí sim a gente começa a ver o poder de uma linguagem escalável, com muito dinamismo mas sem deixar que “vire zona”.

2 thoughts on “Scalando, parte 2

  1. Pingback: Gilliard Cordeiro » Blog Archive » Scala: Implicit Converters

  2. Pingback: Gilliard Cordeiro » Será que o Java 7/8 faz falta mesmo?

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>