Sunday, December 03, 2006

More Stupid Tiger Tricks

I'm trying to collect some idioms for programming with Java 5 generics, with varying degrees of usefulness, that I found scattered on different places. [Edit]Hopefully I fixed the whitespace problems on the code snippets. Blogger is pretty weak for working with source code, but now I believe its alright. As a bonus, I threw in another item on the end. I guess I'm all out of generic idioms for now.[/Edit]

Union Types


Let's say I have a method to remotely execute some task. Its signature could be something like this:

public void compute(Runnable task)

But there is a catch, we would also like the argument to be Serializable. We could define an application specific interface like

interface Task extends Runnable, Serializable {}

but that would prevent us from accepting exiting classes that just happened to implement both of these interfaces. Java 5 allows us to specify exactly that with union type bounds for type arguments. It is actually pretty easy, see how we apply the technique to our example:

public <T extends Runnable & Serializable> void compute(T task)

I advice you to reserve this idiom mostly for interacting with third-party code. If you find the need to require union types frequently on your own code, there is probably something wrong with the design and some refactoring may be in order.

Casting with a type token


"Know and use the libraries" is one of the most important items in Josh Bloch's Effective Java book. It's also one I frequently violate, only to regret later on. For instance, had I read class Class' documentation attentively, I could have avoided many unchecked cast warnings. An example is in order:

class Registry {
public void storeObject(Object obj) { }
public T getObject(Class<T> ofClass) {}
}

I think this class is pretty self explanatory, it can store an object indexed by it's class and later retrieve the stored instance. The challenge is how to implement it without upsetting the compiler. (And yes, @SupressWarnings("unchecked") is cheating...). For those of you who have done your homework and read class Class' javadocs you know this isn't much of a challenge after all, see:
   
class Registry {
private Map<Class<?>, Object> objects = new HashMap<Class<?>, Object>();

public void storeObject(Object obj) {
objects.put(obj.getClass(), obj);
}

public <T> T getObject(Class<T> ofClass) {
Object instance = objects.get(ofClass);
return ofClass.cast(instance);
}
}

Safe dynamic class loading


More goodies from Class class. Let's see how to dynamically load using generics. Again, we start with an example:

public interface ServiceLocator {
public Executor getExecutor();
}

This is a simple service locator that knows how to get an Executor instance. Service locators usually find service instances from naming services (in a Java EE environment, JNDI is most likely). But a simpler implementation could just read concrete class names from a properties file and instantiate the objects on construction. Let's take a shot at coding this up:

class ServiceLocatorProps implements ServiceLocator {
private Executor executor;

public ServiceLocatorProps() throws Exception {
Properties props = new Properties();
props.load(getClass().getResourceAsStream("services.properties"));

String executorClassName = props.getProperty(
"java.util.concurrent.Executor");

Class<Executor> executorClass =
(Class<Executor>) Class.forName(executorClassName);
//WARNING!!
this.executor = executorClass.newInstance();
}

public Executor getExecutor() {
return executor;
}

}


Please don't pay attention to the inexistent Exception handling. The point here is that the call to Class.forName() is followed by an unckecked cast and the compiler will promply warn us all about it. The correct code for this constructor would be:

public ServiceLocatorProps() throws Exception {
Properties props = new Properties();
props.load(getClass().getResourceAsStream("services.properties"));

String executorClassName = props.getProperty(
"java.util.concurrent.Executor");

Class<?> loadedClass = Class.forName(executorClassName);
Class<? extends Executor> executorClass = loadedClass.asSubclass(Executor.class);

this.executor = executorClass.newInstance();
}

Subclass to capture type arguments


This last one is not so much an idiom as a kludge to get around erasure. Most people who use generics in their code eventually get bitten by this problem, as I already bitched about before. Anyway, sometimes one way to get around the information loss is to subclass the parametrized type. This is because, unlike an instance of a generic type, a subclass does retain type argument information. For an example, see the problem recently posted by Ted Neward and my proposal for a "solution" (its the last one). A much more sophisticated example, including reflection, can be found on this Caelum blog post (it is in portuguese, but the code is understandable).
[Edit: one more item:]

Generic Wrapper


I guess I can't properly call this one an idiom, since I only saw it once. I'm talking about the java.sql.Wrapper interface in jdbc4. It is s a 2-method interface: isWrapperFor(Class<?> iface) and <T> T unwrap(Class<T> iface). It exists to expose extensions to a predefined API in a typesafe manner. I would serioulsy consider something like this when implementing a public extensible API/SPI.
[/Edit]

Saturday, November 18, 2006

Disabilng subversion localization.

I had a hard time figuring how to disable svn localization features, so this post is just here in the hope that it can help the next person who encounters this problem. Its very simple, actually. Subversion obeys traditional Unix internationalization environment variables even when running on other operating systems, so one can just set the LC_ALL env variable to "C" (without the quotations marks) and the console tools will work just as installed on an english language system.

Why did I want to do this? One reason is that the locale handling is broken by default on Windows, but its not hard to fix this (just set the APR_ICONV_PATH environment variable to the \iconv directory on your svn installation). I did it because some build scripts found in the wild work by parsing svn output and localization obviously screws them up.

I'm (still) not dead

This is where I should be excusing myself for being "way too busy" to update my blog and promising that things will get back to "normal" soon. But I'm not actually so busier than usual, it's just that every post I thought about writing ended up larger that expected and taking more time that I have available (I think my brain needs a better job scheduling algorithm). I'll post something decent soon. Or not so soon, but eventually...

Anyway, this is not a link blog - I just can't seem to be able to write any commentary with less than ten lines - but I maintain my del.icio.us bookmarks reasonably well. So, if anyone is curious about stuff I find interesting take a look at http://del.icio.us/rafaeldff.

I'll end this useless keepalive post with another musical recommendation: The Jam is a pretty cool old band. Listen to "Eton Rifles", it sounds a bit like a Clash song.

Wednesday, September 20, 2006

Arrays, genéricos e o maldito compilador (e uma errata)

Há um tempinho eu postei aqui uma dica que deveria facilitar a construção de listas e maps em Java. A idéia básica era escrever uma classe utilitária com métodos estáticos usando varargs e genéricos que depois seriam importados (via static imports) nas classes clientes. Algo assim:

public class CollectionUtils {
public static List listWith(T... elements) {
return Arrays.asList(elements);
}
...
}

Isso até que funciona direto, mas não posso dizer o mesmo sobre a implementação do mapWith. Este método retorna um java.util.Map criado a partir de uma série de pares chave-valor. Cada par é representado por um objeto da classe Map.Entry<K,V> criado usando outro método estático. Assim um uso do mapWih poderia ser:

Map<NumeroNatural, String> numeros = mapWith(pair(ZERO, "0"), pair(ONE, "1"));

Para programar o mapWith eu usei o mesmo truque do listWith, receber os argumentos (os pares) via varargs:

public static <K, V> Map<K, V> mapWith(Map.Entry<K, V>... entries) {
HashMap<K, V> map = new HashMap<K, V>();
for (Map.Entry<K, V> eachEntry : entries)
map.put(eachEntry.getKey(), eachEntry.getValue());
return map;
}

Essa definição de método compila normalmente, mas todas as chamadas à ele receberão um warning do compilador avisando que um array genérico está sendo criado. "Huh? Mas eu não estou criando porcaria de array nenhum!", foi o que eu respondi para o compilador. Depois de me assegurar que ninguém me viu conversando com o javac.exe, que aliás é um interlocutor bem antipático, pesquisei um pouco e descobri que varargs no Java 5 usa arrays por baixo do pano. "Ok, mas qual é o problema de criar um array de tipo genérico?". Bom, vamos por partes, como diria o Leibniz. Primeiro precisamos lembrar que arrays são covariantes no tipo do seu elemento; veja:

String[] sa = new String[10];
Object[] oa = sa;
oa[0] = new Integer(5);

Esse trecho de código compila, mas a última linha atira uma exceção (ArrayStoreException) em tempo de execução. Ou seja, a VM precisa testar se o tipo do array é adequado ao tipo de um objeto sendo inserido nele. Agora, pense nesse trecho:

Class<String>[] ca = new Class<String>[10];
Object[] oa = ca;
ca[0] = new Integer(8);

Aqui, o compilador reclama já na primeira linha que não consegue criar um array de Class<String>. E ele não consegue por um detalhe da implementação de generics no Java: a técnica escolhida para especificar tipos genéricos, chamada "Erasure", remove as informações sobre os parâmetros de tipo (as coisas dentro de <>) ao produzir o bytecode. Como a linguagem não teria informação suficiente para fazer as verificações necessárias quando da inserção de elementos no array, a solução adotada é simplesmente proibir arrays de tipos genéricos.

Agora vou fazer como todo bom mau programador e botar a culpa na linguagem por um bug que eu escrevi. Começando pela escolha de implementar generics via erasure, que significou sacrificar naturalidade em nome da tal "migration compatibility". Volta e meia acontece de eu escrever algum statement que parece perfeitamente inocente e o compilador me chama de burro (ou quase isso). Outra decisão que não me agradou foi a de usar arrays como base dos varargs, em vez de algo menos capenga como Iterable ou Collection. E minha última reclamação é sobre a covariância dos arrays, que enfraquece a tipagem estática a troco de nada (alguém sabe de um bom caso de uso para arrays covariantes?).

Wednesday, August 30, 2006

A taste of Scala

I've recently been trying to learn Scala, a programming language developed at a Swiss university. It has many (many!) cool features, such as seamless interoperability with Java - a result of being compiled to JVM bytecodes -, strong support for functional programming, sophisticated object oriented characteristics and a strong static type system. Rather than continue listing the language's capabilities I will, instead, share a personal use case.

OK, so one of the first items things I looked at was the unit testing framework, SUnit. It comes bundled in the standard library in the scala.testing.SUnit package and is dead simple. I hope the following snippet is self-explanatory enough:
object StackTest {
import scala.testing.SUnit._

def main(args:Array[String]): Unit = {
val tr = new TestResult
new TestSuite(
new Test01,
new Test02,
).run(tr)

for(val f <- tr.failures())
Console println f
}

class Test01 extends TestCase("pushing an element onto an empty stack") {
override def runTest() = {
val stack = new Stack()
val element = "asdf"
stack push element
assertEquals(stack.peek(), element)
}
}

class Test02 extends TestCase("popping an element from a stack") {
override def runTest() = {
val stack = new Stack()
val element = "asdf"
stack push element
stack.pop()
assertEquals(stack.isEmpty, true)
}
}
}

It really is pretty simple; the whole testing framework sits on a single 200-line file. But is also is a bit verbose, isn't it? All those inner classes, cluttering the code... I tried to simplify things a bit. Here is what I came up with:
import sorg.testing._;

object StackTests extends Tests with ConsoleDriver {
test ("pushing an element onto an empty stack") {
val stack = new Stack()
val element = "asdf"
stack push element
assertEquals(stack.peek(), element)
}

test ("popping an element from a stack") {
val stack = new Stack()
val element = "asdf"
stack push element
stack.pop()
assertEquals(stack.isEmpty, true)
}
}
I think it looks better. Sort of like those DSLs that are so fashionable these days... But the really cool thing is that it only took a couple dozen lines of code and a couple of hours to extend SUnit. Mind you that someone really proficient in Scala could probably do it much more quickly. See the whole unit testing domain specific language: (the name is almost larger than the code itself :)
package sorg.testing;
import scala.testing.SUnit._;

abstract class Tests extends Test with Assert {
type TestExp = () => Unit;
var tests = List[Pair[String,TestExp]]();

def test(desc: String)(t: => Unit) : Unit = {tests = Pair(desc,()=>t) :: tests};

override def run(tr: TestResult) = {
for (val Pair(desc, expression) <- tests) new TestCase(desc) {
override def runTest() = {Console println "running (" + desc + ")"; expression()}
}.run(tr)
}
}
trait ConsoleDriver extends Test {
def main(args:Array[String]): Unit = {
val results = new TestResult
Console println "running tests..."

this.run( results )

if (!results.failures.hasNext)
Console println "Success!";
else {
Console println "The following tests failed:";
for(val each:TestFailure <- results.failures)
Console println (each.toString + ":\n" + each.trace);
}
}
}

Expressiveness and power, what more can one ask of a programming language?

Tuesday, August 15, 2006

Componentes e objetos

Muito já se escreveu sobre a distinção entre programação orientada a objetos (OO), desenvolvimento baseado em componentes (CBD) e service oriented architectures (SOA). Isso significa que este post vai indubitavelmente chover no molhado. Entretanto, não se pode dizer que o assunto está resolvido e acabado; eu só espero confundir menos do que esclareço.

Como bem lembrou Philip Calçado, não podemos nos esquecer de que estes conceitos todos foram concebidos em contextos diferentes por grupos distintos e sem maiores preocupações com uma coesão formal. OO por exemplo, tem origem na Noruega, mas foi conceptualizada de maneira mais completa no laboratório da Xerox em Palo Alto, sendo reificada numa sequência de protótipos da linguagem Smalltalk. O líder desse projeto foi o lendário Alan Kay, que também tem a distinção de ter cunhado o termo "programação orientada a objetos". Kay, que na época já tinha um background em biologia e matemática, imaginou um paradigma computacional centrado em entidades similares a células biológicas, que se comunicavam trocando mensagens químicas. Apesar da paternidade reconhecida, não há consenso sobre o que seria a definição de OO, então trabalharei com uma definição improvisada (não confie muito). Um objeto é caracterizado por:
  1. Comportamento. Ele é capaz de receber invocações* de outros objetos para efetuar uma computação e (a) responder com um outro objeto ou (b) alterar o seu estado. Para efetuar a computação, o objeto pode invocar outros objetos. Ele obtém acesso a estes objetos de três maneiras: criando - "instanciando" - novos objetos na hora, usando uma referência que é parte de seu estado ou recebendo-os no momento em que é invocado.
  2. Estado. Um objeto pode armazenar um conjunto de referências para outros objetos. Através dessas referências ele pode invocar comportamento dos outros objetos conforme descrito no ítem anterior.
  3. Identidade. Mesmo que o estado todo de um objeto se altere, ele ainda existe como a "mesma" entidade.
  4. Ciclo de vida. Um objeto é instanciado em algum momento, quando ele passa a existir (manter uma identidade). Ele também pode ser destruído em algum momento. As linguagens de programação que seguem o paradigma OO apresentam uma grande variedade de mecanismos para instanciar e destruir objetos (classes, protótipos, prefixos, gerenciamento manual de memória, coletor de lixo, ...).
Enquanto que objetos são entidades fundamentais de um modelo computacional, componentes são apenas uma alternativa para atender a certos requisitos encontrados em algumas aplicações. Essa afirmação pode parecer polêmica, por isso apelo para uma autoridade maior, Ralph "GoF" Johnson, que expôs bem essa consideração num post recente em seu blog:
It is important to realize the "components" are not technology, they are a customer requirement. Customers want to add on to their software system by adding a new component, and adding this component must not require that they change their existing system. It is like adding new speakers to a stereo system.
Ou seja, componentes são nada mais que uma unidade de extensão e/ou substituição dinâmica de um sistema de software. O adjetivo "dinâmico" é uma daquelas palavras que são jogadas por aí em contextos tão diversos a ponto de perder o seu significado, mas a noção aqui é bem simples: estamos considerando alterações realizadas em ambiente de produção, feitas por operadores do sistema. A partir dessa caracterização, podemos dezfazer uma série de confusões:
  • Uma aplicação pode usar componentes sem ser baseada em componentes. Isto é, é possível definir alguns poucos pontos de substituição e/ou extensão mantendo o resto do software completamente estático. Dentre os muitos exemplos estão a maioria dos programas de desktop; um exemplar típico é o Photoshop e seus plugins. É claro que também encontramos diversos casos no extremo oposto - aplicações totalmente baseadas em componentes - veja por exemplo os últimos servidores de aplicações e IDEs.
  • Um sistema pode ser modular mas não baseado em componentes, na acepção que estamos considerando. Basta que seja composto de partes pouco acopladas.
  • Não faz muito sentido projetar um sistema baseado em componentes fortemente acoplados. O motivo é simples: componentes fortemente acoplados exigem que um desenvolvedor independendente se preocupe com grandes porções do sistema, o que prejudica o atendimento ao requisito de reconfigurabilidade. Mesmo no caso de aplicações que limitam seu uso de componentes a extensões localizadas, é uma boa prática estabelecer uma interface reduzida entre o núcleo do sistema e os componentes.
  • A definição sugerida exige apenas que um sistema possa ser reconfigurado em ambiente de produção, não é necessário que ele tenha a capacidade de sofrer alterações enquanto está executando. Não obstante, existem algumas aplicações onde isto é um requisito real e soluções tecnológicas sofisticadas são necessárias.
  • O professor Johnson deixa bem claro que tecnologias como COM ou EJB não são necessárias para um sistema que use componentes. A rigor, para antender ao requisito delineado é suficiente um suporte tecnológico mínimo. Podemos imaginar até um sistema onde a escolha de componentes é feita através da substituição de arquivos objeto (em java seria um .class) no diretório de instalação.
  • Apesar do ponto anterior, é frequente que um sistema com componentes se beneficie de uma infra-estrutura básica para auxiliar na solução de problemas comuns como: controle do ciclo de vida, gerenciamento de dependências, suporte à modos de comunicação entre componentes, parametrização local (propriedades), etc.
Como já demorei muito mais do que imaginava para escrever este post - incluíndo um tempo desperdiçado reescrevendo uma boa porção do texto quando eu fiz besteira e perdi o draft - vou deixar para uma entrada futura a discussão sobre sistemas distribuídos e serviços.

Uma última observação: o leitor atento notará que a palavra reuso não aparece nesse post; isso não foi um acidente.

* Estou evitando a terminologia baseada em "mensagens" que é padrão em Smalltalk para evitar mais confusão.

Sunday, August 06, 2006

Hmph.

Ontem eu fiz besteira e perdi o post que estava escrevendo. Para encher linguiça até que eu me anime a escrever sobre tecnologia de novo, aproveito para listar umas bandas que eu ando curtindo. Aviso que não sou um daqueles nerds indie que vendem a mãe e hipotecam a avó para pagar a assinatura do NME, por isso não espere novidades extraordinárias:
  • Art Brut. Banda excelente e engraçada... Recomendo a música "My Little Brother", mas na real todas as músicas deles são legais.
  • Arctic Monkeys. Enchi o saco de todo mundo dizendo que o hype é exagerado. A banda é boa, e não é culpa dos caras que uma parte da mídia resolveu coroá-los como O Próximo Strokes (que era para ser O Próximo Nirvana...). Ouça "Cigarete Smoke".
  • A TPM passou e o Yeah Yeah Yeahs lança um disco mais calmo. IMHO, continua ótimo, mas eu sinto falta da Karen O se esgoelando. Música: "Turn Into", uma baladinha.
  • Já o segundo disco do Dresden Dolls, "Oh, Virginia", me pareceu bem mais fraco que o primeiro....
  • Rock Rocket. Mais um expoente (que palavrinha tosca) do rock etílico (pleonasmo?) brasileiro. Ouçam o hit "Ninfomaníaca".
  • Chega por hoje.

Thursday, August 03, 2006

Model View Confusion

The development community is a strange beast. And I'm not talking about hitchhiker's jokes and D&D tournaments. The strangeness I'm referring to is that many of the most talked about concepts are used in ways that differ a lot from their original, intended, meanings. Many patterns fall in this category, but one is specially abused: MVC. This is one of the first TLA's a java developer learns on his first days in the craft. By "learn", I really mean "kinda sorta knows that it has something to do with that Struts crap".

I was in that exact position a few years ago (crazy coincidence, innit?). After a few hours of wikistumbling through c2.com, I got considerably more confused. The next step I took was to chase down a copy of the first published description. Google wasn't my friend in this process* and I had to resort to the old-fashioned route of requesting a loan from some - the only one, in fact - college library that had the aug/1988 copy of the Journal of Object Oriented Programming. I only half-understood it because at the time I wasn't conversant in Smalltalk. But I undestood enough to see that MVC was somewhat different from what the Java guys talked about.

One big difference is that Smalltalk-80 was self-hosted - it was it's own operating system**. This meant that input device processing was a responsability of objects. And that, my little friends, is how the Controller came to be. Quoting the paper:
In particular, controllers coordinate the models and views with the input devices and handles scheduling tasks. (...) Class Controller does include default scheduling behaviour...
What motivated me to write this entry was Fowler's superb article on GUI architectures. In there you'll find a thorough discussion on MVC and its subsequent variants. It will be a chapter on the upcoming second volume of his Patterns of Enterprise Application Architecture.




* Of course, now the bastard is online here.

** This control obsession can be seen still today, just look at Squeak's UI and fondly remember the glorious Win3.1 days...

Monday, July 10, 2006

add1

Faltou um.
  • Little Schemer é um bom livro?
    • É sim.
  • Para que?
    • Para aprender programação funcional.
  • Como ele ensina programação funcional?
    • O leitor vê uma pergunta, pensa um pouco, compara com a resposta do livro e depois faz a mesma coisa para a próxima pergunta.
  • Depois de terminar o livro, dá para sair programando em Lisp ou Scheme?
    • Não, não dá.
  • Ok, o livro não foca na prática. Mas, ele é ser forte na teoria?
    • Não muito. Conceitos como continuations e closures são trabalhados informalmente. Cálculo lambda não chega nem a ser mencionado.
  • Hmm. Então porquê ele é um bom livro?
    • Para aprender programação funcional.

Wednesday, July 05, 2006

963 páginas

Como estou sem criatividade para escrever alguma coisa original, vou tomar a tradicional solução bloggeira. Não, não vou postar fotos de animais de estimação, estou falando da outra solução bloggueira para preguiça intelectual: book reviews.

Admito que esse semestre foi bem fraco para leituras; só tive tempo para quatro livros:
  • "Bartleby, o Escriturário", do Melville, edição de bolso da L&PM. Não manjo de crítica litéraria e não sou presunçoso o suficiente para pensar que poderia análisar um clássico da literatura. Só digo que gostei muito, e recomendo. Ah, e ele é curto, assim como esse comentário.


  • "The Design of Everyday Things", do Donald Norman. Obrigatório para qualquer pessoa que esteja envolvida na criação de qualquer coisa, o livro fala da interação entre as pessoas e os objetos cotidianos (ou não tão cotidianos). Norman é um especialista em psicologia cognitiva que se interessou pelo estudo da usabilidade quando foi chamado a compor o conselho designado para apontar as falhas que levaram ao desastre de Three Mile Island. Ele descobriu que muito do que se costuma apontar como "falha humana" na verdade é causado por objetos que foram projetados sem levar em conta as pessoas que terão de operá-los. No caso da usina, encontrou problemas como séries de controles muito parecidos produzindo ações muito diferentes e alarmes que disparavam com tanta frequência que acabaram por ser ignorados no momento crítico. O livro discute esse exemplo e muitos outros, mas não é apenas um catálogo de erros de usabilidade. Ele também formula uma teoria de como as pessoas aprendem a usar as tais "everyday things" e apresenta uma série de conselhos para quem têm a responsabilidade de projetar seja uma usina nuclear, uma chaleira ou um software. Para instigar mais o apetite, são estes os conselhos: 1. Use both knowledge in the world and knowledge in the head; 2. Simplify the structure of tasks; 3. Make things visible: bridge the gulfs of execution and evaluation; 4. Get the mappings right; 5. Exploit the power of constraints, both natural and artificial; 6. Design for error; 7. When all else fails, standardize. Algum ponto negativo? Ficar folheando até o fim para ler as notas é um saco (e eu sou simplesmente incapaz de pular e deixar para depois...).

  • "Secrets & Lies", do Bruce Schneier. O autor é um criptógrafo notório, e um dos livros anteriores dele, o Applied Criptography, é a obra mais popular sobre o assunto. Aqui, ele trata de segurança digital de uma maneira ampla. O livro é muito bom, mas eu esperava algo diferente do que encontrei. Usando um termo encontrado repetidas vezes no texto, pode-se dizer que o livro não é destinado a security experts, nem a aspirantes à expert, mas sim à quem contrata os tais security experts. Isso não implica que a leitura seja inútil para quem tem perfil técnico, pois as idéias do Schneier sobre como abordar a segurança são sempre muito inteligentes e às vezes até um pouco surpreendentes. Uma destas é a a constatação de que a comunidade de segurança computacional põe ênfase exagerada em medidas preventivas, como se fosse possível se proteger de todo e qualquer ataque futuro, e negligencia as outras fases: detecção e resposta. Outra tese relacionada é resumida no mantra "segurança não é um produto, é um processo", que é uma daquelas coisas que parecem óbvias até que alguém nos chame a atenção para as ramificações. Enfim, não me arrependo de tê-lo lido e até recomendo, mas a falta de profundidade me incomoda. Especialmente quando percebo que o autor sabe tratar de assuntos complexos sem alienar o leitor leigo; prova disso é o primoroso capítulo 6, que explica em linhas gerais as principais técnicas criptográficas sem entrar em detalhes teóricos, mas conseguindo ilutrar bem a mecânica básica e a importância de cada ferramenta.
  • Little Schemer, da MIT Press. O comentário sobre esse fica para depois. Só adianto que é um ótimo livro mas não compre sem dar uma folheada antes...

Sunday, June 04, 2006

Stupid Tiger Tricks

[EDIT 22-09-2006: There is a bug on the mapWith code below. I explain what happened here.]

One of the things that bugs me about Java is that it lacks literal constructs for creating lists and maps*. This, coupled with the profusion of casts, can lead to APIs prefering arrays over collections, which are otherwise much easier to handle. Java 5 (Tiger) brought generics, helping to minimize all those annoying casts, but the verbosity for initializing collections is still an issue. Here is the result of a quick hack to try and improve things a bit:

public class Web2dot0 {
private List<String> buzzwords =
listWith("mashup", "AJAX", "bottom-up", "tagging");
private Map<Integer, String> strategy =
mapWith(pair(1, "Pretty AJAX UI"), pair(3, "profit!"));
...
}

This was implemented using an utility class with the following static members:

public class CollectionUtils {
public static <E> List<E> listWith(E... items) {
return Arrays.asList(items);
}
public static <K, V> Map<K, V> mapWith(Map.Entry<K, V>... entries) {
HashMap<K, V> map = new HashMap<K, V>();
for (Map.Entry<K, V> eachEntry : entries)
map.put(eachEntry.getKey(), eachEntry.getValue());
return map;
}
public static <K, V> Map.Entry<K, V> pair(K key, V val) {
return new ImmutablePair<K, V>(key, val);
}
public static final class ImmutablePair<K, V> implements Map.Entry<K,V> {...}
...
}

The listWith(), mapWith() and pair() methods were then static imported in the client class.

* And now the geniouses at the JCP want to create XML literals. That reminds me of a comment I read in Taenembaun's computer organization book, saying that if programmers could have their way in the chip design process, opperations like branchAndDoPayroll would be part of the ISA.

Thursday, May 25, 2006

Clássicos III

O próximo na esporadiquíssima* série de papers clássicos é o "No Silver Bullet: Essence and Accidents of Software Engineering" (html, pdf), escrito pelo Fred Brooks, um pioneiro da engenharia de software. Entre outros feitos, ele foi o gerente do desenvolvimento do IBM OS/360, escreveu o também clássico livro The Mythical Man-Month, onde cunhou a famosa lei de brooks e recebeu o prêmio Turing de 1999.

O paper "No Silver Bullet" foi publicado originalmente em 1986 e tem, mais do que seus antecessores nesta série, um certo ar antigo, decerto produto das muitas referências a tecnologias oitentistas. Isso não significa de modo algum que esteja obsoleto, visto que as décadas posteriores confirmaram seguidas vezes a tese principal do artigo. Essa tese pode ser resumida na constatação de que muitas das dificuldades envolvidas no processo de desenvolvimento de software são essenciais, portanto é impossível que qualquer avanço técnico consiga uma melhoria de sequer uma ordem de magnitude na produtividade, confiabilidade ou simplicidade.

Essa proposição sem dúvida é importante, e serve como um bom antídoto para os venenos dos vendedores de elixires mágicos para a produtividade. Eu, entretanto, aprecio mais outros aspectos desse texto: ele não é longo, mas é pontuado por pérolas de experiência. Cito algumas, começando por esse trecho extraído da discussão sobre a complexidade de software:
Software entities are more complex for their size than perhaps any other human construct because no two parts are alike (at least above the statement level). If they are, we make the two similar parts into a subroutine--open or closed. In this respect, software systems differ profoundly from computers, buildings, or automobiles, where repeated elements abound.
Uma diretriz importante no desenvolvimento de software é minimizar a repetição (uma formulação mais imperativa desse princípio veio dos pragmatic programmers - Don't Repeat Yourself - DRY - com acrônimo e tudo). Isso quer dizer que, ao menos em teoria, ao analisar um sistema de grande porte, todo detalhe é significativo; cada linha deve representar o produto de um trabalho intelectual. Compare com um grande arranha-céu**, por exemplo. Não se trata de menosprezar os construtores - certamente existem desafios consideráveis em projetos civís dessa escala - mas me parece claro que o trabalho intelectual para construir dois andares não é o dobro do envolvido na construção de apenas um. Não obstante, os pedreiros*** tem de suar a camisa para de fato botar estes tais andares em pé. Incrívelmente, muita gente não percebe essa diferença fundamental e tenta tratar programadores como trabalhadores braçais, limitados a seguir as instruções de um ser superior ocupando um cargo pomposo (atualmente "arquiteto" é popular, mas "analista de sistemas" já foi o título mais comum).
Ainda sobre complexidade:
Much of the complexity that he [the software developer] must master is arbitrary complexity, forced without rhyme or reason by the many human institutions and systems to which his interfaces must conform. These differ from interface to interface, and from time to time, not because of necessity but only because they were designed by different people, rather than by God.
Em outras palavras "it's the requirements, stupid!". Uma das qualidades do software é a precisão; qualquer programa faz exatamente o que o programador manda, e nada mais. E isso é ótimo, pois abre a possibilidade para enormes ganhos de qualidade ao automatizar processos de negócios. E também é aí que a porca torce o rabo****, pois é surpreendentemente complicado traduzir estes processos, e seus dados, em especificações que computadores consigam entender. Isso porque as informações difícilmente estão em formato suficientemente exato para alimentar o desenvolvimento de software; costumam ser imprecisas, ambíguas e, frequentemente, inexistentes. Essa é razão da pujança de empresas como a SAP e os outros 500 bilhões fabricantes de ERPs.*****

Vou parar por aqui, pois este comentário já esta longo demais. No decorrer do texto, Brooks levanta algumas das principais dificuldades essenciais e desconstrói vários candidados a silver bullet, mostrando como eles tratam apenas das difuldades acidentais, sem aliviar as essenciais. Nesse caminho encontramos uma diversidade de conceitos interessantes (a própria nomenclatura de dificuldades acidentais vs essenciais já é de grande valia) e idéias que instigam reflexão.
EDIT: Esqueci do mais importante: RTFA!


* Será que essa palavra já foi escrita alguma vez antes? Espero que ninguém mais comita esse crime... Eca.
** Já escrevendo em português, às vezes a gente não tem o mesmo cuidado...
*** Tenho a impressão que esse termo não é políticamente correto, mas não consegui arranjar um substituto adequado. Se alguém se sentiu ofendido, por favor escreva um email para bite@me.com
**** Continuando a minha irrefletida e irrefreável busca por originalidade, torço para que esta seja a primeira (e a última) vez que a expressão "a porca torce o rabo" aparece num texto sobre engenharia de software...
***** É bem verdade que nem todas são tão bem-sucedidas como a SAP...

Wednesday, May 10, 2006

Language advocacy

  • Lisp: author makes a successful effort to show Lisp's virtues to the non-initiated. This tends to be a not so easy task, since the sources of Lisp's superiority are the very same things that make it look so alien to programmers versant in ALGOL descendant languages.
  • Scheme: Great language, naive writing. Scheme deserves better.
  • Smalltalk: This article highlights very well a lot of the points that struck me personally when I first came into contact with Smalltalk.
  • More smalltalk: "Smalltalk: Requiem or Resurgence?" I'm not very optimistic... But, who knows?
  • More lisp: When you get bored with reading about computer programming languages, you can relax, sit back, and enjoy this video about computer programming languages.

Thursday, April 27, 2006

(RDBMS) v (¬RDBMS)

My previous post was in response to Keith's writing regarding the possibility of escaping object relational mapping problems by avoiding altogether RDBMS's. The discussion of where and how relational databases are to be used turned out to be a hot topic on the blogosphere* these days, mostly fueled by Tim O'Reilly's nine-part saga on data management in Web 2.0* companies. The three most salient points I got from Tim's posts were: there is a preference for clustering by data partitioning instead of general replication strategies; predictably there also is a preference for open source relational db systems (MySQL being the front-runner); and there is a belief that read-mostly data is better served from flat files (sometimes with sophisticated replication mechanisms) than from RDBMS'. There is a lot of interesting info there, I recommend reading the whole thing.

Speaking of read-mostly data, I found this cool project via Joe Gregorio. It's a relational database system being built at MIT optimzed for this kind of data. Another interesting system for dealing with simple read-mostly data is Google's BigTable, that seeks to achiveve high throughput by adopting of a very simple data model, whithout integrity constraints nor transactional capabilities, structured in a highly distributed and fault-tolerant fashion (as would be expected of google). It also features native data versioning support.

PS: Yes, this post was written in english. At least something approaching what one might call "english"... The short motive is that I just felt like it. The longer one is that I need to practice my english at least as much as I need to practice my portuguese, and writing practice is one of the primary reasons for this blog's existence (yes, I'm a selfish bastard).

* buzzword count: 2. I'll try to do better next time.

Thursday, April 20, 2006

Don't forget to use your brain

Keith Braithwaite, one of the postmodern programming guys, wrote this interesting blog post. I tried to comment on the post page but, sadly, my wise and deep words didn't find it's way to the world wide web. So, in the immortal words of the prophet, I'm just gonna have to wing it here.

A reasonable amount of bytes and brain cells have been lost, over the years, on the object-relational mapping battleground. Keith's argument is that many of those casualties could be prevented if developers let go of the RDMS dogma and adopt simpler solutions when possible. If the requirements don't indicate a heavy load of concurrent updates, then there is no need for powerful (and expensive) transactional capabilities. He gives as an example an online shopping web site. Usually, additions and changes to the product catalog don't need to be immediately reflected to the users. Also, the possibility of simultaneous modifications can be negligible here. He proposes* that, in cases like this, the data can be made available in a simple format locally, on the front-end servers. Updates can be periodically pushed from a central database to the front-ends.

The only nit I have to pick with his post is when he talks about queries:
"For instance, if you want to get hold of an object that (you have to know) happens to be backed by the underlying database then what you do is obtain an entry point to the data, and search over it for the object you want. Unlike the navigation that you might expect to do in an object graph, instead you build...queries. (...)"
Yes, building a whole query just to get hold of one object reference is too much trouble and a violation of DRY (parenthesis: this sort of thing eases somewhat this pain, minus all the factories and spurious abstractions). But I think he overlooks a bit the fact that often we really need to do a query. It's frequently part of the problem domain, not the solution domain, to put it in other (more pompous) words.

I view queries as inherently declarative operations (given x information, get me Y more information). As such, they are better expressed through declarative means. So, aCollection select: aBlock in Smalltalk is better than the equivalent for loop in Java. Still in OOland, Evans and Fowler's Specification pattern is even better for more complex cases. Advancing to the logical conclusion, a language specifically designed for searching would be even better. Unfortunatly, SQL falls short of reaching this goal in practice, because of the mess that is integrating it with the rest of the application. Microsoft's linq project is an intriguing technology in this space.

Anyway, what I wanted to point out is that there is no one size fits all software architecture and every project needs to be thought out** by a team who knows what it's doing and isn't afraid to think outside the vendor-supplied box.


* barring possible misunderstandings of my part.
** This is not a defense of BDUF

Monday, April 10, 2006

Friday, April 07, 2006

Code Contest

Essa história começou aqui. A segunda edição está proposta no blog do Daniel Quirino. Achei uma boa para brincar com a Time and Money library, do Eric Evans. Aqui vai a minha submissão:

package cc;

import java.io.File;
import java.util.TimeZone;

import com.domainlanguage.time.*;
import com.domainlanguage.timeutil.Clock;

public class DeleteOldFiles {
public static void main(String[] args) {
Clock.setDefaultTimeZone(TimeZone.getDefault());
CalendarInterval lastWeek =
Duration.weeks(1).subtractedFrom(Clock.today()).through(Clock.today());

for (File f : new File("D:\\tmp\\backup").listFiles()) {
TimePoint lastModified = TimePoint.from(f.lastModified());

if (lastWeek.includes(lastModified))
f.delete();
}
}
}

Só não tente rodar, porque não vai dar certo. Encontrei um bugzinho na biblioteca e mandei um patch, daqui a um tempo a versão no CVS deve ser atualizada.

Tuesday, April 04, 2006

Faltam só 3h32min3s

Mensagem do Richard Gabriel para o Hillside group, via Grady Booch:
I got this from a colleague: "As you may have noted, on Wednesday, at two minutes and three seconds after 1:00 in the morning, the time and date will be 01:02:03 04/05/06. Unless you are very young, or live a very long time, this is probably your one chance to observe this date. Whoop it up."

Monday, April 03, 2006

Holismo

Às vezes acontece de uma boa palavra ser coagida a defender idéias ruins. Estou pensando no caso da palavra "holismo", figurinha comum nos lábios de charlatões e enganadores. Apesar disso, é esclarecedora a idéia, que remonta a Aristóteles, de que um sistema pode ser melhor compreendido como um todo do que como a soma de suas partes.

Uns meses atrás eu resolvi descobrir o que Extreme Programming tem de tão especial e comprei o livro canônico: Extreme Programming Explained, do Kent Beck, pai da criança. É um livro bastante persuasivo, repleto de argumentos incisivos. Um dos que mais me chamaram a atenção foi a idéia de que a otimização de cada etapa de um processo de produção não leva, necessariamente, à otimização do processo inteiro. Esse argumento é aplicado à engenharia de software para apoiar a tese de que uma abordagem metodológica baseada em ciclos curtos produzindo resultados úteis, mas "incompletos", é superior à tradicional visão do processo como uma sucessão de longas fases, cada uma gerando montes de "artefatos". Podemos encarar o ponto de vista dos xispeiros como uma aplicação do princípio do holismo ao desenvolvimento de software.

Também é possível enxergar o holismo em outro membro da família desajustada que é a ciência da computação. O princípio end-to-end, do qual já tratei por aqui, é claramente uma defesa da otimização global de sistemas no âmbito da arquitetura de software.

Eu comecei a pensar essa bobajada toda quando estava discutindo com um amigo sobre a eficiência de aplicações web. Ele afirmou que vale a pena recorrer à técnicas extremas para atingir o propósito de maximizar a performance de uma aplicação web, visto que quanto mais um harware for aproveitado, menor é o custo para servir o mesmo número de usuários (ou "maior é o número de usuários que podem ser servidos por um mesmo hardware", se você é do tipo gosta de entender as coisas ao contrário). Meu amigo chegou a defender que é válido escrever sites web como dlls ISAPI em C++... O raciocínio dele está formalmente correto, mas as considerações econômicas são mais amplas que uma minimização simples de função de custo. Se tratamos de uma situação competitiva, a capacidade de reagir rápido aos avanços da concorrência é vital. No caso de desenvolvimento de software interno, a pressão competitiva é indireta, se manifestando nos famosos requerimentos mutantes que forçam a evolução constante e eterna da funcionalidade das aplicações.

Bolha versus mercado.

Leiam o ótimo post do pcalçado.

Tuesday, March 28, 2006

Maladies of the information age

Steve Jenson was right, it did hit too close to home...

Monday, February 27, 2006

Clássicos II

Prosseguindo com a série de posts sobre artigos clássicos da computação, avançamos duas décadas a partir do End-to-end Arguments, para chegar à OOPSLA 98. Se o leitor considera um paper publicado em 98 recente demais para ser considerado um verdadeiro clássico, fique tranquilo que o autor é Guy Steele. Co-autor da especificação da linguagem Java, chairman do comitê de padronização do Common Lisp, co-criador do Emacs e autor da linguagem Scheme, ele é um dos pesquisadores mais importantes na área.

O clássico desta vez é Growing a Language, texto de uma palestra apresentando uma abordagem evolucionária para o projeto de linguagens de programação. Steele defende a tese de que novas linguagens devem ser conceptualizadas como um pequeno núcleo de primitivas a ser extendido transparentemente pelos usuários. Com o tempo a linguagem cresce através das adições da comunidade, em um modelo muito similar ao dos projetos de software de código aberto. Sua argumentação é feita ressaltando especialmente os aspectos sociais da adoção de linguagens de programação, sugerindo vários pontos em comum com a literatura de metodologias ágeis de programação (design incremental) e patterns (descrições contextualizadas de abstrações). Uma menção importante é feita ao artigo Worse Is Better, do Richard Gabriel, que está na minha fila de clássicos. O artigo não aborda diretamente a motivação estética para linguagens inerentemente extensíveis, provavelmente com bons motivos.

"Growing a Language" também lembra um assunto que está, tipo assim, super na moda: as Domain Specific Languages (DSLs). A idéia básica é tratar parte do projeto de software como projeto de uma linguagem versando sobre o domínio da aplicação. A partir daí existem várias interpretações: tem quem defenda que usuários finais, especialistas no negócio, sejam responsáveis por editar as DSLs para acompanhar mudanças no ambiente; outros pensam que , de alguma maneira, todo desenvolvimento de software é a criação de DSLs; outros ainda acreditam que as DSLs são melhor expressadas como linguagens gráficas de modelagem. E os programadores de Lisp dizem, como sempre, que já fazem esse tipo de coisa há décadas de maneira muito mais elegante e não entendem todo o fuss atual. O texto do Steele o situa entre aqueles que enxergam todo projeto de sofware como projeto de DSL, e ele defende que a linguagem de programação básica seja criada tendo isto em mente.

Tudo isso é muito importante, mas o texto do paper também é imperdível por seu genial desenvolvimento metacircular. Não entendeu? RTFA.

Friday, February 17, 2006

Clássicos

Minha mais nova tentativa de tentar transformar esta porta de banheiro binária em algo minimamente útil será uma série de comentários breves sobre artigos clássicos na área da computação. O gerador de clichês dentro do meu cérebro, condicionado por milhares de horas de hipnóticas aulas de redação, não me deixa continuar este post sem explicar o que entendo por "artigos clássicos", então vamos tentar uma definição por oposição: não são selecionados de acordo com qualquer métrica acadêmica; não são os 'melhores' textos, apenas os mais importantes dentre os pouquíssimos que lí; não busco cobrir nenhum escopo prévio, especialmente não tenho ilusões sobre cobrir o todo da ciência da computação; e nem todo clássico é velho (pelo menos aqui...).

Para facilitar, vou listar aqui os links para os posts nesta série:
Começo pelo "End-to-end arguments in system design", artigo de Reed, Saltzer e Clark, todos professores do MIT, publicado inicialmente em 1981. A tese principal do artigo é que em sistemas de software em rede arquitetados em camadas, adicionar funções nas camadas inferiores pode trazer pouco ganho ou até se revelar prejudicial. Isso porque a camada de mais alto nível tem mais informações sobre o problema em questão, às vezes tanto que ela precisa implementar completamente a funcão independente dos esforços dos níveis inferiores. Essa discussão está muito teórica e convido o leitor a read the f*cking paper para aproveitar os exemplos catalogados pela turma do Reed.

Numa primeira leitura, o artigo parece tratar de uma consideração arquitetural relevante, mas não mais importante que as tantas outras restrições que tiram o sono de desenvolvedores há decadas. O que torna o argumento dos professores especialmente importante é que entre suas implicações econômicas encontramos (suspense...) a Internet. Antes que alguém dispare o seu cliente de email na direção de um inocente, aviso que, sim, eu sei que a internet e o TCP/IP já existiam há um bom tempinho quando os nobres professores escreveram o paper, mas seu objetivo é descrever um fenômeno observado, não apresentar nenhuma nova e mirabolante idéia. Aliás, a maioria dos artigos nesta série não versam sobre novidades e invenções. Do que eu estava falando mesmo?

Pop(), pop(). Ok, a Internet. Pergunte a qualquer engenheiro de comunicações o que ele acha sobre a estrutura da internet e ele contentemente respoderá que é um negócio que até funciona mas poderia ser muito melhor. Ela não contempla, por exemplo, segurança (ip-sec?) e garantia de qualidade de transmissão (QoS? RSVP? ATM?). É, em outras palavras, uma rede bastante burra. Entretanto, é inegável que modificou violentamente a economia e a sociedade na última década (para o bem e para o mal). Imagine um cenário alternativo onde QoS fosse implementado de ponta a ponta na internet, permitindo que se reservem faixas de banda para determinadas aplicações. Finalmente video-on-demand seria uma realidade! O preço a pagar é que a rede deixaria de ser um campo neutro onde empresas florescem a todo tempo (que meigo) para se tornar um mercado imobiliário onde quem pode pagar mais por naco de banda excluí a priori qualquer competidor potencial. Mais sobre esse assunto dos próprios autores do clássico ou, mais eloquentemente, do senhor Lessig.

Esse último texto foi apresentado ao congresso americano pedindo legislação que impeça o comprometimento da isonomia entre aplicações, contrariando os planos das grandes empresas telefônicas. Donas dos backbones da Internet, elas ameaçam cobrar para dar preferência a determinados serviços em detrimento de outros nas "suas" redes. Confirmando sua vocação para clássico, vemos que as observações do "End-to-end arguments in system design" continuam, mais que atuais, prementes.

Monday, January 02, 2006

Mais um comentário sobre a rant mais recente do Joel Spolsky

A rant: The Perils of JavaSchools

Dou a palavra ao sr. Hoare e ao sr. Meyer:
Pointers are like jumps, leading wildly from one part of the data structure to another. Their introduction into high-level languages has been a step backwards from which we may never recover. (Charles Hoare)
You can either have software quality or you can have pointer arithmetic, but you cannot have both at the same time. (Bertrand Meyer)
E daí? Nada.

Sunday, January 01, 2006

Organizando a informação

O primeiro post real desse blog, há quase um ano, foi sobre o desperdício que é a produção de grande parte de toda informação digital em formas não estruturadas. Aparentemente, eu não sou o único a enxergar nisso um problema. Avi Briant, mais conhecido como o criador do framework Seaside, também reconheceu o problema, arregaçou as mangas e pôs-se a trabalhar numa solução. Como um bom empreendedor, materializou sua idéia como um produto, agora em fase de testes fechada, chamado Dabble-DB. É difícil explicar o conceito e peço ao leitor que assista ao vídeo de uma demonstração para entender melhor, mas podemos descrevê-lo como um banco de dados estruturado incrementalmente que se apresenta na forma de uma aplicação web. Um usuário interage com o dabble informando somente os dados que precisa para completar a tarefa imediata, e o esquema estrutural do banco vai evoluindo aos poucos, paulatinamente ampliando sua abrangência.

Duas curiosidades técnicas: o dabble é escrito em smalltalk/seaside e o gerenciamento dos dados consiste em manter os bancos inteiros dos usuários ativos em memória, o que me lembra o discreto projeto zeppelin do Tim Bray(sun).

Vou parar por aqui e continuar o assunto nos próximos posts. É uma tentativa de reavivar um pouco mais esse blog moribundo.