Skip to content

O caso de Enumerable infinito

No ultimo ano tenho trabalhado bastante com o conceito de  lambdas e monads. Em particular o conceito de monads aplicados a filtrar e transformar coleções.
Comecei a pensar como isso poderia ser trazido para o Middleheaven. Isto foi antes do java 7  e nesse momento não existia java 8 e o conceito de Stream.

A verdade é que esse conceito é maior que as linguagens e está presente em muitas delas. Aliás está presente na Apache Commons Collection desde o inicio.

O conceito é simples, você começa com uma coleção qualquer, transforma para um objeto que suporte operações de filtro/transformação – normalmente usando algum tipo de decorador. Configura os filtros/transformações através de métodos encadeados, como se fosse um construtor e no fim executa tudo de uma vez só em uma mistura de Command e Composite.

A vantagem disto é que o fluxo pode ser bem complexo mas apenas um único for será executado. É a versão moderna de “for is evil”. Tradicionalmente em java fazemos todas as iterações explicitamente usando for, mas existem grandes vantagens em utilização repetição implícita. A principal delas é que podem existir otimizações ou o uso de processamento paralelo pode ser usado

O principal era ter uma classe que suportasse operações de filtro/transformação. Hoje existe o Stream em java, mas na época o Middlheaven ganhou o Enumerable. Foi difícil achar um nome porque a maioria dos nomes que se adequariam já estão tomados. O nome foi emprestado da API de C#. O Enumerable é um Iterable que permite mais operações, como filter e map.

Conceptualmente o Enumerable pode ter infinitos elementos. Isto é uma propriedade comum neste tipo de objetos e é seguida pelo objeto Stream do java 8 e a sua contraparte IEnumerable no C#. Pelo menos em teoria.
Mas ao implementar a API comecei a notar que muitos dos algoritmos dependiam de usar o atributo size para saber quando elementos existiam no Enumerable. Ora, isto, conceptualmente estava errado, porque não ha tamanho quando ha infinitos elementos.
O IEnumerable e o Stream resolvem isto com exceções, mas não me parecia uma forma elegante. Procurei por ai, como resolver isto.

Achei uma palestra meio louca e assustadora sobre Scala, em que no meio de muita confusão ha duas ou três coisas interessantes. Uma delas, é exactamente lidar com o tamanho infinito.
O truque é encapsular o tamanho em uma classe e utilizar a class para prever o tamanho, mas sem nunca o calcular, pois. Isto nos trás ao conceito de tipos de tamanho: determinável e desconhecido.
O tipo determinável pode ser ainda : infinito, contável e calculável. Infinito significa que não existirá fim para a enumeração. Contável significa que podemos obter o tamanho com complexidade O(1), como acontece se realmente o nosso Enumerable apenas está encapsulando um List – que acontece a grande maioria das vezes.
E finalmente calculável, que significa que é possível terminar a enumeração e saber quantos elementos tem, mas ao custo de iterá-los a todos (uma operação que nos custará O(n) onde n é tamanho que queremos determinar).

Mas ai nasceu um outro problema. Para os tipos contável e calculável é possível ter uma propriedade que nos retorna o tamanho. Contudo, sendo que podem haver muitos elementos, que tipo de dado escolher? int ? long ? bigInteger ?

Parece um pouco exagerado ter que usar BigInteger para retornar o tamanho de Enumerable, mas o fato é que realmente pode ser muito grande. Grande demais para um int ou um long. Mas isto é em teoria, na prática, a maioria dos casos é o encapsulamento de um Collection qualquer que é a base da iteração. Na prática int seria suficiente, mas em teoria não, o que quebraria a API.

Seria possível remover o conceito de que o Enumerable pode ser infinito, isto simplificaria muito a API e poderiamos remover o conceito de que pode ter mais elementos que Integer.MAX_VALUE, contudo o futuro é de máquinas com 64 bits ou mais. Poderíamos usar long. É um bom valor quando estamos retornando dados de um banco, por um tempo será suficiente. Contudo, mesmo assim travaríamos o contrato da API.

A solução foi apelar para um outro componente do MiddleHeaven, o Numeral. Em particular aquele que representa um numero inteiro.. Numeral é uma representação abstrata de um número com três sub tipos até agora, inteiro, decimal e complexo – representados pelos tipos BigInt, Real e Complex. O BigInt é um Numeral que pode representar números inteiros.
Dei-me conta que este nome é meio confuso. A ideia do nome é porque ele representava um numero de 64 bits maior que um int e por isso o Big. Mas se confunde com BigInteger. Sendo assim resolvi mudar o nome para Int.  E assim temos os tipos Int, Real e Complex.

O MiddleHeaven desacopla o modelo abstrato da quantidade da sua representação.  Int, Real e Complexa são classes abstratas e a classe que realmente representa o valor não é pública. A implementação concreta para representar o Int era a classe LongInteger que usava um long como forma interna de guardar o valor, para representação e base da aritmética. Contudo ocupa 64 bits a todo o momento, quando a maior parte das vezes só precisamos de 32 bits, ou menos. O ideal era ter um tipo que fosse aumentando conforme necessário. Isto, aliado à necessidade de ter um range extenso para o tamanho de um Enumerable trouxe a ideia de um range dinâmico. Assim o  Enumerable retorna um EnumerableSize que têm vários sub tipos e para os que podem obter o valor do tamanho com exactidão, eles retornam um Int.

Porque a construção de qualquer Int passar por um método de fábrica valueOf é o método que determina qual a classe que melhor se adéqua ao valor a representar. O mais baixo é um int de 32 bits representado pela class Int32 ( é dificil arranjar nomes para tanta classe, então simplesmente coloquei o numero de bits na frente – à lá .NET style. Contudo, porque esta classe é privada ao pacote o nome realmente não interessa muito)
Sempre que for feita uma operação em que se obtenha um numero que não cabe em 32 bits, ou seja, quando acontece um overflow, automaticamente os valores são promovidos a Int64. Se o mesmo acontecer com os valores em Int64, eles serão promovidos a BigInt.  BigInt é a implementação de Int que se utiliza de um BigInteger internamente. Teoricamente BigInteger pode conter um número de qualquer tamanho (a custo de operações aritmética mais lentas). A partir dai, se existir um problema de oveflow uma exceção será lançada. Para que isso acontece é preciso um número seja realmente absurdamente astronômico. Isto permite que o tamanho de Enumerable seja igualmente astronômico.

O próximo passo poderia ser criar e usar Int8 e/ou Int16 e ir promovendo à medida que necessário já que maioria dos números que nos interessam estão nesse limite.

Este caso é muito semelhante ao que a JVM faz com os primitivos. Ela permite que os menores sejam usados como se fossem os maiores através de promoção automática. A diferença é que ao fazer uma operação com um tipo de numero e perante um overflow não acontece uma promoção automática, mas sim algo inesperado. Para todos os inteiros de n bits , ao somar 1 ao maior inteiro de n bits obtemos o menor inteiro de n bits. E ao subtrair 1 do menor inteiro obtemos ele mesmo! Sim. É verdade, obtemos ele mesmo.
Na realidade é pouco mais complicado que isso, porque para fazer cálculos a VM sempre promove os valores para int e depois dos cálculos coloca de volta no tipo original. O problema é que pode ser que o resultado não caiba de volta no tipo original. Não ha nenhum mecanismo de auto-promoção durante os cálculos.Nem poderia, já que os tipos primitivos não são polimórficos.

O que o Int oferece é encapsulamento e polimorfismo para resolver isto. O tipo concreto do objeto passa de um sutipo de Int para o outro sem que o programador note, pois ele sempre trabalha com a classe Int. Assim é sempre garantida a correta representação do valor.Esta propriedade não existia antes com o LongInteger porque , ao ser baseado num long, tinha os mesmos problemas aritméticos que o long primitivo.

Alguns algoritmos passaram a ser impossíveis caso o Enumerable seja infinito, como por exemplo colocar o Enumeble em um ArrayList ( que só pode conter Integer.MAX_VAlUE valores) ou executar sort(). Contudo, existem rumores para a criação de uma API de arrays de 64bits em futuras versões do java o que indica
que o limite do array não é em si mesmo, constante.

Tudo isto pode parecer desnecessário, mas é realmente um problema conceptual que pode danificar uma API (como vimos no exemplo do Scala). A nova API de Datas e Tempos do java é a prova que são necessárias APIs mais corretas e com melhores abstrações e a nova api JSR 354 de Money and Currency que está na forja para o Java 9, segue o mesmo caminho. Inclusive cai no mesmo problemas de tipos abstratos para valores numéricos, sendo obrigada a definir um novo tipo de Number, o Decimal, que seria um objeto capaz de presentar o mesmo que um long tanto em casas inteiras como em casas decimais.
Um BigDecimal seria demasiado devido à performance necessária, e um double não é suficiente para esta façanha pois não pode representar corretamente todos os números neste intervalo. Todas as potencias negativas de 10 são dizimas infinitas em base 2, o que leva a problemas para representar 0.10 e 0.01 centavos. Aliás este problema é toda a base para a necessidade uma API de Money e por isso que o MiddleHeaven também tem a sua.
Infelizmente a api de Money está planejada para trabalhar com arredondamentos em divisões intermediárias, enquanto a API do Middleheaven usa o padrão Ratio para evitar divisões em todos os passos até ao fim. Para isso o objeto Money faz uso de um Real para o seu valor, pois o Real é implementado com padrão Ratio como a divisão de dois BigInteger (e agora acabando de escrever isto, acabo de ver que Real deveria ser implementado como a fração de dois Int e não de dois BigInteger…).

Com o tempo verifiquei que a necessidade do valor do Money ser um Real não deriva apenas do padrão Ratio, e sim do fato que embora, conceptualmente o Money represente um valor finito de centavos, em algumas contas- como o calculo de juros , essa regra não
é simples de aplicar pois a aritmética exige o uso de números reais nos calculos resultando em números com várias casas decimais. Bom, mas a história do Money é outra história para outro dia…

Achei que o caso do refatorar o Int era um caso interessante de como uma parte do MiddleHeaven – o Enumerable – ajudou a encontrar problemas conceptuais em outra parte – o Numeral, em particular o Int. Isto porque quis me manter fiel ao conceito de um um Enumerable pode ter uma valor astronômico de elementos, inclusive, infinitos, mas sem cair no custo de usar sempre BigInteger – bom, pelos menos não até que seja realmente necessário.

 

MiddleHeaven e Java 8

O projeto Middleheaven nasceu como uma biblioteca auto-suficiente para criar qualquer tipo de programas em java. A experiencia mais interessante do projeto o design. O design começou quanto estavamos no java 1.4 , e nessa época a API padrão do java era bem pobre em certos aspectos. Ao longo do tempo as APIs JSE e JEE eoluiram muito e hoje muitas as coisas que o middleheaven oferece já estão na api padrão. Exemplos são um codificador de base 64 , api de temos e datas, api de arquivos, entre outras.

Eu sabia que chagaria o tempo em que as apis do middleheaven poderiam ficar obsoletas, mas tendo em consideração a primeira diretiva que é fazer o programa depender exclusivamente do middleheaven de forma a encapsular toda a tecnologia, a api do middleheaven teria que evoluir junto com a api padrão. Por exemplo, agora que o  java 8 contém um codificador base 64, a api do middleheaven pode simplesmente delegar as funcionalidades e não ter que implementar esse recurso por si mesma. A api de arquivos se integra com a api de servelets para manipular arquivos obtidos por upload do usuário , a api padrão do java faz isso. A api de datas e tempos pode agora confiar todos os calculos à nova api de datas e tempos mantendo apenas os objetos . O plano funcionou.

Muito da API do middleheaven foi construida com base no conceito de lambda, mesmo quando ele ainda era só um rumor. Agora, graças à funcionalidade do java 8 de entender lambdas como SAM types podemos usar lambdas com a API de enumerable. Um recurso que muito provavelmente será útil é o recurso de virtual extension methods quando formos para a versão 2.

Pois, é isso ai. Embora a versão 1 não seja usada cabe transformá-la para tirar proveito do java 8 e como tal é preciso renomeá-la para versão 2.

Uma das dificuldades do projeto é a parte de UI. Existe alguma integração como algumas ferramentas como Freemarker e Vaadin, mas não ha um modelo coeso para a UI. Isto principalmente se deve ao problema do modelo 2 (action based) vs modelo 3 (event based) . O modelo 2 é completamente suportado , mas o modelo 3 não.

Em alguns pontos ha que se afastar da api do java pelo menos enquanto não existirem extensiom methods verdadeiros como em C#. Um desses exemplos é o conceito de Optional / Maybe. O Maybe do middlheaven pode ser estendido pelo middleheaven enquanto o Optional está nas mãos da Oracle que não o levou muito longe. Sim, ambos partilham problemas – como o fato de que a linguagem não garante que a própria variável to tipo maybe/optional não será nula – mas a api de maybe pretende fazer uso extensivo de lambdas e se integrar com a api de enumerab le para permitir um código mais simples e direto.

Depois de investir tanto tempo no projeto middleheaven é custoso pensar em investir ainda mais para o manter a par com o java 8 quanto na realidade ele não é um framework tão usado quanto outros, contudo depois de muito pensar chego à conclusão que o investimento já foi feito, mais vale tirar partido dele. Tal vez programando o framework em outra linguagem ou usando outra linguagem durante o uso serviria para desacoplar ainda mais o framework e realmente fazer as aplicações não dependerem da linguagem ou plataforma subjacente. Depois de procurar não achei uma linguagem que pudesse permitir isto e minha conclusão se aproxima mais e mais da conclusão que para dar certo deveria existir uma “middleheaven language” em que o framework serve como a API que desacopla a tecnologia. Contudo esta conclusão levaria ainda a mais trabalho que não é compensado pelo numero de usuários do framework. Portanto continuarei nos passos de manter o middleheaven desacoplado da teclogia subjacente, mas ainda sendo programado e usado via linguagem java e correndo na plataforma java. Contudo cada vez mais tentando se afastar de algumas falhas que existem na api padrão e que outras linguagens/apis fizeram melhor.

Javadoc Disponivel

Finalmente  consegui colocar o javadoc no google code e o link aqui no blog.

O Javadoc está mais completo em umas áreas que outras e está em inglês, mas espero que ajude a documentar e entender a API. O Javadoc mostra todas as toolboxes juntas, então não se assustem com o numero de classes e interfaces.

Lista de Discussão

Está aberta a lista de discussão para usuários do projeto MiddleHeaven.Esta lista é aberta para que usuários possam tirar suas dúvidas, possam compreender melhor o projeto e para poderem expressão suas opiniões e devolver feedback.

 

Para participar da  lista de discussão do MiddleHeaven você tem que se logar numa das sua contas do google, acessar o endereço https://groups.google.com/forum/#!forum/middleheaven-users-pt e apertar o botão para se juntar ao grupo.  Todos os pedidos de acesso serão concedidos, é apenas uma medida para evitar spam.

Espero que com esta lista o projeto possa realmente dar e receber da comunidade e evoluir para uma ferramente útil e utilizável no dia-a-dia de todos.
Por enquanto estou limitando a uma lista em português e para usuários onde todos podem participar.
No futuro, se houver necessidade será possível criar listas para os desenvolvedores e em inglês.
Espero por vocês lá.

 

Seis anos e muito para fazer

Eu venho trabalhando no projeto MiddleHeaven ha 6 anos, mesmo quando não tinha esse nome. O projeto nasceu da experiencia em cria frameworks nas empresas por onde passei e identificado padrões mais usados em sistemas reais. Claro que o tempo livre para me dedicar ao projeto diminui todos os anos já que o projeto não tem retorno financeiro o que significa que ainda é um hobbie.

Mas em 6 anos muita coisa mudou no mundo java. Do java 5 passamos para o 7  e logo, logo o 8. A plataforma EE evolui muito e se tornou muito mais parecida com o Spring. A produtividade do MiddleHeaven está na manutenção e na migração de plataforma e não necessáriamente na criação do projeto. Não que demore para por o sistema no ar, mas a configuração básica do Middleheaven não é tão básica assim, já que é preciso ligar todas as peças do puzzle. Isto levou à construção de mecanismo de injeção e o spring não foi usado porque na época não existia um mecanismo programático para o usar, o que já existe hoje.  E com o conceito de Environment (diferente do que foi adicionado no spring 3.0) que ligado em paralelo ao mecanismo de injeção as coisas ficam um pouco complexas. Em cima disso o MiddleHeaven ainda oferece suporte a modulos e controle de licença, o que complica ainda mais a inicialização.  Funciona, mas neste ponto com certeza é complexo de explicar e sem entender este ponto é mais complexo alguém se juntar ao projeto.

Então o  esforço agora é parar um pouco nas funcionalidades e tornar o framework mais amigável e mais simples. As estruturas core do projeto estavam muito complexas e de certa forma isso minava o progresso porque a toda a hora o grilo falante está lá para alertar do problema.

Outro problema é o escopo. Para ter um framework completo end-to-end que se possa adaptar aos ambientes e aos estilos de design do usuário e ao mesmo tempo ha que seguir padrões, fazer documentação , melhores práticas, etc… é complexo. O passo marco inicial é ter uma versão do Middleheaven que possa prover estrutura de sites web rodando apenas em web-container. Isto atenderá a maioria dos projetos.  Aqui, outros frameworks com mais reputação e mais tempo no mercado serão a competição. Mas a proposta do MiddleHeaven é diferente e espero que isso possa, pelo menos, aguçar a curiosidade de alguns.

Falando de diferenças e completando o meu post anterior, o processo de reescrita da camada de persistencia está terminado. Agora existem duas camadas onde antes existia uma. A maioria dos projetos hoje em dia lida com ORM e com o mapeamento dos objetos de dominio para as tabelas do banco de dados. Sempre foi preocupação do MiddleHeaven não restringir o processo a tabelas de banco de dados relacionais. Por outro lado, a experiencia mostra que em certos casos não queremos usar ORM. O cenário clássico é quando estamos fazendo relatórios. Queremos ter mais flexibilidade em como montar os dados.

Com base nesta destinção nasceu a toolbox de DataSet. Esta nova API permite controlar o mapeamento de dados logicos (chamados dataset) para os dados fisicos (tabelas para começar). Em cima da API de DataSet fica a API de ORM que já existia, mas agora todo o processamento de SQL e afins foi para a API de DataSet.

Por quê isto ? Porque ficou patente que nem sempre o dominio iria ser mapeado para tabelas e escrever um ORM para cada tipo de persistencia seria mais demorado e complexo ( embora ainda, seja uma estratégia possivel) e por outro lado gostaria de permitir o use de outras formas de persistencia NoSQL, em particular o BigTable do App Engine.  O App Engine mostrou que o padrão de desenvolvimento de ORM com banco de dados nem é a única forma e que mesmo seguindo as API padrão como JPA podemos ficar de fora da corrida. Ou seja, o problema da portabilidade de aplicações entre ambientes é exactamente o problema que o MiddleHeaven quer endereçar e não  fazia nenhum sentido estar amarrado a SQL.

Ainda falta trabalho a ser feito na api de DataSet e integração com o ORM (API de DomainStorage) mas a estrutura e o refactoring já estão no lugar.

Por falar de refactoring, também ficou claro que manter todo o projeto num só jar era overkill. Então o projeto foi dividido em diferentes sub-projetos (usando o maven) para permitir melhor organização, controle de dependencias e ajudar ao deploy usando menos e menores jars.

Após esta ultima remessa de alterações no core e completar funcionalmente a API de DataSet podemos pensar em ter a versão 0.1 finalmente vendo a luz do sol.

Seguindo em frente

Como já devem saber o MiddleHeaven está sendo utilizado para suportar o site http://www.javabuilding.com o que significa que funciona🙂

O mecanismo de processamento de requisições HTTP é abstraido das classes de servlets e utilizado por um mecanismo baseado em ação.  Devido à injeção de dependência e injeção de parâmetros apenas precisamos escrever uma classe que atua como controlador/presenter e configurar quais url ela irá responder.  Todas as configurações são feitas em código. A razão para isto é que são configurações dos mecanismos que não mudarão no deploy, por isso não ha necessidade de as deixar em arquivos separados. Uma possivel futura é utilizar scripts groovy e javascript para as configurações mantendo o melhor dos dois mundos.

O foco do MiddleHeaven para GUI é prover um mecanismo comum para web e desktop orientado a componentes, mas enquanto isso não fica pronto com a toolbox de processamento e o processador baseado em ações já dá para fazer alguma coisa.

Junto com isso temos algumas tags próprias para ajudar a criar as páginas, em particular o forEach que aceita qualquer Iterable e não apenas List.

Entretanto o storage toolbox continua sendo remodelado. O problema é a utilização transparente em ambiente multi-thread e a possibilidade de utilizar o hibernate por detrás dos panos. A realidade é que esse não era o objetivo inicial, mas o mecanismo genérico está atrazado. Além disso é um exercicio para o design do toolbox já que será, provavelmente, o mais usado de todos é necessário que esteja bem solto para evoluir.

Por fim , queria deixar um pedido de comentário sobre as toolbox existentes e quais features seriam interessantes, como por exemplo, se deveria mesmo haver suporte ao hibernate.

No céu do meio

O projeto Middleheaven continua  a passo curtos. Muita gente tem demonstrado o seu apoio. Obrigado.

A identidade do projeto talvez esteja um pouco nebulosa porque até agora o que ha para mostrar é apenas o código e um conjunto esparso de texto falando de umas coisas chamadas toolboxes. Toolboxes são como pedaços da API, como no JSE temos o JCF ,  I/O e Threads que na realidade se encaixam mas as vemos como partes separadas.

O projeto começou por ser apenas um repositorio de código que eu utilizava repetidamente. Coisas como copia de arquivo e utiltários vários. Depois passou a ser um framework. Um conjunto de coisas prontas, mas complexas. À medida que construia outros frameworks mais simples eu pensava em poder levá-los até ao limite e incorporá-los no framework. Hoje, o MiddleHeaven não é mais um framework e sim uma plataforma de desenvolvimento. Uma arquitetura e design padronizada com partes extensiveis e plugáveis e um conjunto de plugins e extensões já preparadas.

O MiddleHeaven pode-se comparar ao Spring no espirito de facilitar a programação e conexão de partes da aplicação.  Tal como o Spring o MiddleHeaven conta com um motor de injeção automática, mas à diferença do Spring ele não tem definição baseada em XML. Na ralidade não tem porque não foi implementado. O MiddleHeaven delega essa parte a um objeto especifico que pode ser implementado de muitas formas. Por agora, anotações são a base – pode escolher entre as do próprio MiddleHeaven ou as padrão como @Resource.

Ao contrário do Spring que utiliza directamente outros frameworks e não oferece nenhum encaspulamento – no sentido que você ainda tem que saber usar esse outro framework – o MiddleHeaven oferece as suas próprias estruturas e design que pode ser extentido com implementações diversas baseadas em diferentes frameworks de terceiros.  Enquanto que no Spring estes frameworks são le reason de être do motor de injeção, no MiddleHeaven eles são apenas detalhes de implementação que podem mudar ou ser substituidos.

Um outro framework com que poderiamos traçar um comparativo é o JCompany. O JCompany é um framework proprietário com uma versão livre que vai um passo além do Spring oferecendo um certo encapsulamento em torno de APIs de terceiros visando o máximo possivel aderir a padrões de mercado. O JCompany oferece muito já pronto e está mais para aplicação costumizável do que para framework propriamente dito. Contudo o conceito por detrás é muito semelhante ao do MiddleHeaven. A grande diferença é que – além da licença – o MiddleHeaven não corre atrás dos padrões de mercado. Esse é um objetivo explicito. Os padrões serão incorporados quando necessário e normalmente por debaixo dos panos já que todo o conceito do MiddleHeaven é exatamente abstrair toda essa parafernália de padrões e tecnologias que mudam constantemente.

O MiddleHeaven é fortemente baseado, não apenas em padrões , mas também em conceitos. As abstrações do MiddleHeaven tendem a ser ir o mais longe possivel para que possam ser reutilizadas o máximo possivel.  Em alguns pontos isso pode significar menor performance ou uma complexidade extra que o programador considera desnecessária, contudo, é sempre possivel não utilizar o modelo do Middleheaven ou usá-lo parcialmente. O desafio é que o programador goste de usar o modelo do MiddleHeaven porque ele é natural.

Embora eu tenha começado dizendo que o MiddleHeaven está caminhando a passos curtos isso é porque estou me referindo a todas as capacidades que foram sonhadas para ele. Contudo ele já faz muita coisa. Já é possivel desenvolver uma aplicação web no estilo do Spring MVC , o ponto que falta para fechar o ciclo e poder libertar uma versão beta ( ou talvez 0.2-alfa) é a persistencia; que está sofrendo uma remodelação.  Depois vem a parte de interação com o usuário que permitirá aplanar a diferença entre o mundo web e o desktop. Esta é a funcionalidade realmente nova do MiddleHeave,n mas  para chegar lá o básico tem que estar no lugar e ser sólido.