Domain Specific Languages em ação

Em diversos momentos sentimos a necessidade de utilizar uma linguagem para atacar um problema mais específico. Utilizar Java ou C# nesse tipo de problema pode gerar uma enorme quantidade desnecessária de código. Veja um exemplo que passamos na Caelum:

Set<Strategy> strategies = new HashSet<Strategy>();
Indicator<Double> close = new ClosePriceIndicator(timeSeries);
for (int i = 1; i <= 50; i++) {
  Indicator<Double> tracker = new EMAIndicator(close, i);
  Strategy strategy = new 
      IndicatorCrossedIndicatorStrategy(close, tracker);
  strategies.add(strategy);
}

No nosso caso, esse trecho de código deve ser compreensível para analistas de negócio, que não são necessariamente programadores, muito menos possuem conhecimento de Java.

Domain Specific Languages é o nome dado a prática de se criar pequenas linguagens para resolver um problemas bem específicos. Elas existem em dois sabores: as externas, que criam uma linguagem própria, e as internas, que na verdade utilizam um subconjunto de instruções de um linguagem já existente e utilizada no sistema.

Entre alguns clássicos exemplos de DSLs internas temos o uso da Criteria do hibernate, o uso do ruby nos arquivos de build do rake. Entre as DSL externas, temos as macros do excel e o xml do ant.

Utilizando a api de scripting do java 6, passamos a usar ruby (através da JRuby) para escrever essa parte da lógica de negócios, e nosso código em java ficou assim:

(1..50).collect{|x|
	Tail::IndicatorCrossedIndicatorStrategy.new(close,
		Tail::EMAIndicator.new(close, x))
}

Criando algumas factories, conseguimos chegar a um código muito mais simples:

(1..50).collect{|x|
	cross(close, ema(x))
}

Logo, estamos próximos de chegar a algo parecido com uma linguagem natural:

x de 1 a 50
       quando cruzar (fechamento, ema(x))

Para tal, uma das possibilidades seria usar um dos compiladores de compiladores existentes para Java, porém isso daria muito trabalho.

O Rodrigo Kumpera sugeriu escrever o próprio parser, como fez o Gilad Bracha no Small Talk. Tanto o Rodrigo quanto o Renato Lucindo citaram o spirit++, que faz isso para C++. Uma pena não existir algo equivalente para o Java.

Phillip Calçado recomendou fazer um teste para saber se devemos ou não melhorar ainda mais essa DSL. O teste consiste em colocar a linguagem natural ao lado da DSL ruby e ver se o especialista consegue fazer a ponte entre uma e outra. Exemplo:

(1..50).collect{|x|
	cross(close, ema(x))
}
x de 1 a 50
       quando cruzar (fechamento, ema(x))

Se o especialista no domínio não entender a semelhança entre os dois códigos, é necessário aprimorar a DSL em questão.

Tags: , , ,

8 Comentários

  1. Phillip Calçado "Shoes" 21/09/2007 at 16:38 #

    Um problema é a barreira idiomática. DSLs em Ruby funcionam muito bem quando são em inglês (domínio e código).

  2. Diego Pires Plentz 21/09/2007 at 20:04 #

    Se o especialista no domínio não entender a semelhança entre os dois códigos, é necessário aprimorar a DSL em questão.

    Ou trocar o analista 🙂

  3. Tiago Albineli Motta 25/09/2007 at 16:44 #

    Concordo com o Phillip, muitas vezes o analista de negócios pode não fazer facilmente essa ponte exatamente por falta de conhecimentos no idioma.

  4. Paulo Silveira 25/09/2007 at 16:51 #

    Tiago, Phillip, basta abrir a classe Range do ruby e adicionar um método pega que delega para collect! Voces se esquecem que não é java! 🙂

  5. Max Mustang 28/01/2011 at 12:37 #

    3 anos depois e esse post ainda me diverte, com certeza é um dos melhores do Paulo

Deixe uma resposta