(blog ‘lucindo)

um dia eu aprendo a programar

Arquivo de Junho de 2007

Closures

Existe a possibilidade de que na próxima versão de Java a linguagem ganhe suporte a closures (mais aqui, e aqui). Pelo que andei vendo acho que dá para simular algo parecido com closures em C# também usando delegate. O mais legal de tudo isso é ver o que as pessoas acham que são closures e como usam.

Um exemplo disso é o texto de Martin Fowler, que usa Ruby como linguagem para explicar o que é closure.

Tá então closure é uma função que eu posso “salvar” umas variáveis e retornar outra função. Que ótimo! Agora não vou for ter que ficar fazendo inner class e me preocupar com tudo seja final quando for implementar meu callback! E na boa, é só pra isso que closure vai ser usado em Java se um dia entrar na linguagem.

Mas closure vai muito além disso e infelizmente pouca gente vai continuar usando para construir abstrações. Porque o que falta em textos como o do Martin Fowler é dizer como usar isso para construir uma propriedade de closure, ou seja, uma operação ou conjunto de operações fechadas sobre si mesmas, adicionando assim um nivel de abstração.

O exemplo clássico de closure usada dessa maneira aparece no começo do SICP. Livro altamente recomendado. Se possível veja os videos também. Tudo disponível online.
O exemplo é mais ou menos assim. Lisp quem três primitivas que manipulam pares:

  • cons: une 2 elementos em um par (um cons)
  • car: retorna o primeiro elemento do par
  • cdr: retorna o segundo elemento do par

Usando Ruby, vamos dizer que temos essas três operações. Um cons pode unir dois elementos quaisquer, independente de tipo. Assim:

car(cons(1, 4.2)) # retorna 1
cdr(cons(1, 4.2)) # retorna 4.2

Com isso podemos construir listas, como feito em Lisp, porque podemos fazer cons de cons. Uma maneira (convensão) de se fazer isso é assim:

# representa [1, 2, 3, 4]
lst = cons(1, cons(2, cons(3, cons(4, nil))))

Com essa representação de lista podemos construir funções como por exemplo mapcar (que é bem parecida com map de Ruby):

def mapcar(proc, lst)
  if lst == nil
    nil
  else
    cons(proc.call(car(lst)), mapcar(proc, cdr(lst)))
  end
end

Com mapcar podemos fazer inúmeras coisas. Um exemplo bobo é a função scale que recebe um multiplicador e uma lista e retorna outra lista com todos os elementos multiplicados por esse fator:

def scale(factor, lst)
  mapcar(Proc.new {|e| e * factor}, lst)
end

Da mesma forma que escrevemos mapcar podemos escrever outras funções genéricas sobre listas como reduce e filter (em Lisp o efeito de filter é obtido com remove-if).

def reduce(proc, init, lst)
  if lst == nil
    init
  else
    proc.call(car(lst), reduce(proc, init, cdr(lst)))
  end
end

def filter(proc, lst)
  if lst == nil
    nil
  else
    if proc.call(car(lst))
      cons(car(lst), filter(proc, cdr(lst)))
    else
      filter(proc, cdr(lst))
    end
  end
end

Tá, e o que tudo isso tem a ver com closures? Bom, essas funções e a infinidade de abstrações que podem ser construidas a partir delas utilizaram apenas um conjunto fechado de procedimento sobre listas: cons, car e cdr. Sendo que cons é completamente genérica, permitindo que se faça cons de cons. Assim podemos escrever listas, árvores e estruturas cada vez mais complexas tendo como base essas funções simples.
E como cons, car e cdr podem ser implementados? Closures, claro:

def cons(a, b)
  Proc.new { |first|
    if first == true
      a
    else
      b
    end
  }
end

def car(c)
  c.call(true)
end

def cdr(c)
  c.call(false)
end
5 comentários »

rm -rf ??

Tente isso no REPL:

(mapcar (lambda (x) (ignore-errors (delete-file x)))
        (directory “/**/*.*”))
Sem comentários »

MAMP (Mac + Apache + MySQL + PHP)

Com MacPorts:

sudo port install apache2
sudo port install mysql5 +server
sudo port install php5 +apache2 +mysql5 +gd2

Tudo é instalado em /opt/local. É necessário prover um /opt/local/apache2/conf/httpd.conf (talvez copiando de /opt/local/apache2/conf/httpd.conf.sample). Para terminar de instalar o PHP e integrar com o Apache:

cd /opt/local/apache2/modules
/opt/local/apache2/bin/apxs -a -e -n "php5" libphp5.so

Depois adicionar os tipos e definir o arquivo default no httpd.conf

AddType application/x-httpd-php .php .phtml
AddType application/x-httpd-php-source .phps

<IfModule dir_module>
    DirectoryIndex index.html index.php
</IfModule>

Por fim prover o /opt/local/etc/php.ini (existe um exemplo em /opt/local/etc/php.ini-dist).

Para colocar o apache e MySQL no startup:

sudo launchctl load -w /Library/LaunchDaemons/org.macports.apache2.plist
sudo mysql_install_db5 --user=mysql
sudo launchctl load -w /Library/LaunchDaemons/org.macports.mysql5.plist
Sem comentários »