Truques em Scala – Extractors

0 comments

Posted on 5th novembro 2009 by gbenatti in Programação | Sem categoria | scala | truques

Scala muitas vezes me surpreende com a elegancia e integridade da linguagem. O que chamo aqui de integridade, é a forma como a linguagem combina vários conceitos de uma forma uniforme.

Uma das features interessantes de  Scala é que ela permite ao programador extrair informações de objetos usando pattern matching, por exemplo se você tiver o seguinte tipo chamado Email:

  1. case class Email(name: String, domain: String)

Seria possivel testar se um objeto é do tipo Email, e ao mesmo tempo retornar o name e domain com o seguinte código:

  1. unknownObject match {
  2.   case Email(name, domain) =>
  3.     println(name + " at " + domain)
  4.   case _ =>
  5.     println("It’s not an email")
  6. }

No caso de unknownObject ser declarado como:

  1. val unknownObject = Email("georges", "gbenatti.net")

A saida seria “georges at gbenatti.net”

Já se unknownObject fosse declarado como:

  1. val unknownObject: Any = "georges@gbeatti.net"

A saida seria “It’s not an email”

Mas não seria muito legal se fosse possivel fazer essa segunda opção funcionar ? Ai entra a elegância de Scala, você pode definir extractors que permitem que sejam extraidos dados de determinadas estruturas, sejam elas árvores, listas e até mesmo strings.

Um extractor é um objeto Scala que define um método unapply. A responsabilidade do unapply é fazer o matching de um valor e o separar em seus componentes, para o caso acima o extractor poderia ser definido assim:

  1. object EmailStr  {
  2.   def unapply(str: String): Option[(String, String)] = {
  3.     val parts = str split "@"
  4.     if (parts.length == 2) Some(parts(0), parts(1)) else None
  5.   }
  6. }

Definido esse extractor poderiamos reescrever o match da seguinte forma:

  1. unknownObject match {
  2.   case Email(name, domain) =>
  3.     println(name + " at " + domain)
  4.   case EmailStr(name, domain) =>
  5.     println(name + " at " + domain)
  6.   case _ =>
  7.     println("It’s not an email")
  8. }

E ele funcionaria tanto com objetos da classe Email quanto com strings no formato name@domain, ou seja, código final do usuário simples e elegante.

Até mais pp pessoal…

Truques em Scala – Structural Typing

0 comments

Posted on 27th outubro 2009 by gbenatti in Programação | boo | python | ruby | scala | truques

As linguagens dinâmicas como Ruby e Python tem algumas capacidades bem interessantes, entre elas esta a possibilidade de passar um objeto para um método e desde que o objeto implemente os métodos necessários (ex: name, height), o método executará corretamente.

Já as linguagens estáticas normalmente não tem essa capacidade, algumas, como por exemplo Boo implementam essa possibilidade através de dispatch in runtime para os métodos que necessitam dessa caracteristica, já Scala tem uma solução bem elegante.

Por exemplo, digamos que você queira definir um método que espere um objeto que tenha as propriedades name e age, uma forma comumente usada em linguagens estáticas seria a seguinte:

Definir uma interface com os métodos que interessam:

  1. trait BasicProperties {
  2.   def name: String
  3.   def age: Int
  4. }

Escrever as classes implementando BasicProperties e o método recebendo como parametro um objeto do tipo BasicProperties:

  1. class Pet(val name: String, val age: Int) extends BasicProperties
  2. class Person(val name: String, val age: Int) extends BasicProperties
  3.  
  4. def printProp(in: BasicProperties) =
  5.   println(in.name+" is " + in.age + " years old")

E então você poderia chamar o método assim:

  1. printProp(new Pet("Galfildo", 8))
  2. printProp(new Person("Maria", 18))

Se você tivesse uma classe tipo:

  1. class Antique(val name: String, val Age: Int, val cost: Int)

Você não poderia usar um objeto desse tipo como parametro para printProp, já que a classe não herda BasicProperties.

A solução está na capacidade que Scala tem de definir algo que poderia ser chamado de anonymous interfaces, no caso, o método printProp seria escrito assim:

  1. def printProp(in: {def name: String; def age: Int}) =
  2.   println(in.name+" is " + in.age + " years old")

Como se você estivesse definindo como parametro um tipo anonimo, que tem um método name do tipo String e um método age do tipo Int

E então poderia executar:

  1. printProp(new Pet("Galfildo", 8))
  2. printProp(new Person("Maria", 18))
  3. printProp(new Antique("Monalisa", 450, 1000))

Como se ve, um código muito parecido com o escrito em Ruby e Python, mais ainda com uma caracteristica de documentação, já que o método é explicito sobre o que ele necessita.

Truques em Scala – Como fazer parsing de um xml

1 comment

Posted on 27th outubro 2009 by gbenatti in Programação | scala | truques | xml

Ontem eu precisei extrair alguns dados de um xml, e acho que as lições aprendidas merecem ficar registradas, vai que mais alguem nesse mundão precisa fazer algo assim.

  1. <request service="Service 1">
  2.   <extra>
  3.     <entry key="key1">value1</entry>
  4.     <entry key="key2">value2</entry>
  5.   </extra>
  6. </request>

O objetivo era extrair o nome do service como uma string e as entries como um map de key values.

O servico como string é bem facil, considerando que o xml esta em uma variavel serviceXml, teriamos:

  1. val serviceName = (serviceXml\"@service").text

O que isso faz é basicamente pegar o attributo service do nó em questão e com esse dado pegar o nó como text.

A segunda parte é um pouco mais complexa então vamos por parte, primeiro pegamos as entries:

  1. val entries = serviceXml\"extra"\\"entry"

Esse codigo executa basicamente um XPath extraindo do xml o nó “extra” e deste a lista de nós “entry”

Então, para cada entry extraimos uma tupla com o valor da key e o value:

  1. val entriesTuple = for (entry <- entries) yield ((entry\"@key").text, entry.text)

Aqui usamos um for expression para percorrer a lista entries e retornar (usando yield) uma tupla que contem o valor da key e o inner text da entry

E por fim criar um Map com esses dados:

  1. val mapResult = Map(entriesTuple: _*)

Por fim, como construir um Map apartir de uma lista, ou nesse caso de um generator ?

Maps podem ser contruidos usando de um numero variavel de tuplas usando algo como Map(tupla1, tupla2, tuplaN).

Se tentarmos usar Map(entriesTuple) o compilador vai reclamar falando que esse metodo nao pode ser executado com uma Seq[(String, String)], ou seja, uma sequencia de tuplas contendo duas Strings.

Temos que dar um jeito de passar essa Seq como um numero variavel de argumentos, e os desenvolvedores de Scala nos deram uma forma de fazer isso, quando temos que passar uma lista para um método que espera um número variavel de argumentos podemos fazer assim:

  1. Map(entriesTuples: _*)

Essa anotação diz ao compilador para expandir os parametros

Juntando tudo temos:

  1. val mapResult = Map((for (entry <- serviceXml\"extra"\\"entry") yield ((entry\"@key").text, entry.text)): _*)

Muita explicação pra pouco código, assim é Scala

Cheers

Instalando Scala

0 comments

Posted on 10th setembro 2009 by gbenatti in Programação | scala

Para instalar Scala no Mac OS X, baixe o instalador do seguinte link:

http://www.scala-lang.org/downloads/distrib/files/scala-2.7.5.final-installer.jar

Esse link é pra versão 2.7.5 da linguagem.

Execute o instalador, entrando no terminal, vá até o diretório onde se encontra o instalador e digite:

sudo java -jar scala-2.7.5.final-installer.jar

Escolha a linguagem de sua preferência e o local de instalação. No meu caso eu vou aceitar as opções Inglês e /usr/local/

Depois de terminada a instalação, adicione o path de instalação as variáveis de ambiente, edite o arquivo .profile no seu diretório HOME, e adicionar a seguinte linha:

export PATH=/usr/local/scala-2.7.5.final/bin:$PATH

Onde /usr/local/scala-2.7.5.final é o local onde o compilador foi instalado.

Para testar a instalação, feche o terminal e o abra novamente, digite scala -version e você deve ver a mensagem:

Scala code runner version 2.7.5.final — Copyright 2002-2009, LAMP/EPFL

É isso ai, até a próxima.