Red vs Crystal

Vi hoje no Twitter um post do TaQ “An Introduction to Crystal: Fast as C, Slick as Ruby” sobre Crystal. Pelo título fui dar uma olhadinha e achei interessante e, pelos objetivos, achei semelhante a Red (o título pelo menos).

Como conheço mais Red do que Crystal, tentar comparar ambos está mais para presunção do que análise isenta. Então, desculpe qualquer erro nas comparações abaixo, mas resolvi fazer um apanhado de semelhanças e diferenças.

Semelhanças e diferenças (Crystal vs Red)

  • Estágio inicial: Ambas são consideradas beta ou alfa (apesar de ja ser possível fazer muita coisa em Red; acredito que em Crystal também). Red está na versão 0.6.1 e Crystal na versão 0.18.0. Pela abrangência e complexidade de Red, é bem provável que Crystal alcance a versão 1.0 antes de Red.
  • Plataformas: Pelo que vi, Crystal está disponível para Linux e Mac OSX. Atualmente, Red está disponível para Linux, Mac OS X e Windows. Em breve para Android.
  • Instalação: Red não precisa de instalação. É só baixar o executável para a plataforma desejada e executar um script/compilar/usar o REPL.
  • Compatíveis mas não iguais: Red possui a sintaxe parecida com Rebol e Crystal parecida com Ruby. Em algumas situações, Red executa programas feitos em Rebol e Crystal em Ruby. Quem deseja migrar deve estar ciente de que precisará efetuar alterações no código e nada garante que o código funcione sem algum hack.
  • Compilar para código nativo: Com Red, você pode baixar e rodar a versão para Windows e gerar executáveis para qualquer plataforma disponível sem se preocupar com mais nada a não ser a chave -t <plataforma>. É bom deixar claro que Red é composto por duas partes. Red que apresenta grande compatibilidade com Rebol e é de alto nível e Red/System que seria um substituto para o C, compartilhando a mesma sintaxe de Red mas com menos funções. O resultado é que compilando o exemplo da sequência de Fibonacci em Crystal (que está incorreto) o executável ficou com 294K ao passo que em Red/System ficou com 6,4K.  (Crystal foi 0,05s mais rápido que Red). Outro detalhe é que Red pode gerar bibliotecas (.dll, .so, .dylib) que podem ser utilizadas por outras linguagens.
  • Tipagem: Em Crystal, as vezes não é necessário informar o tipo do dado e em outras é (não sei se pode confundir ou não). Em Red não é necessário especificar o tipo de dado mas, se o usuário desejar, poderá especificar para os parâmetros de uma função (pode ser mais de um tipo como integer, string) e assim será gerado erro se um tipo de dado diferente for enviado em vez do erro aparecer apena em tempo de execução. Red/System possui tipagem estática (como pode haver uma interface entre Red e Red/System, existem algumas facilidade para trabalhar com tipos diversos de dados)
  • Interface com bibliotecas: Crystal oferece facilidades para o acesso a bibliotecas escritas em C (não sei se outras linguagens) sem a necessidade de sair da linguagem (escrever um programa em C, por exemplo). Red/System também (e Red por intermédio de Red/System). Como o objetivo de Red/System é ser um “substituto” de C para quem programa em Red, também permite o acesso a syscall (diretamente ao Kernel).
  • Fontes: Ambas linguagens disponibilizam os fontes. Para compilar Crystal você precisa ter Crystal (existem várias linguagens com esta recursividade). Para compilar Red, você precisa ter Rebol (versão comercial). Mas existe a possibilidade de você usar a versão grátis para criar executáveis com os fontes de Red. Ou você pode usar a versão compilada para Windows, Linux e Mac que os desenvolvedores utilizam. Futuramente Red será compilado com Red e a dependência deixará de existir.
  • Espaço armazenamento: Pelo repositório do Arch, diz que eu tenho que baixar 7,5M e, depois de instalado, irá ocupar 57,9M. Para Red eu tenho que baixar 1M do executável e … só. Na realidade, ele cria um arquivo de 500K que é o console (REPL) e uma biblioteca de 10k que são necessários para a execução (considere que em 1M ele já compila para três plataformas diferentes). Não é um item muito relevante mas considerando IoT, pode ser levado em conta (ou se as operadoras limitarem o consumo de dados).

Público alvo

 Bem, qualquer linguagem serve para qualquer um. Vou apenas escrever um pouco para deixar o artigo maior

Crystal

  • Como o ideal não é a compatibilidade com Ruby que tem Rails como carro chefe, certamente não irá abocanhar uma grande parcela de desenvolvedores do Ruby se não for compatível (no artigo diz que não roda Rails).
  • Para outros usuários de Ruby, será necessário analisar com calma o custo/benefício da mudança.
  • Para quem está testando novas(?) linguagens, é uma nova opção para incluir na lista (Go, Rust, Elixir, Red, Crystal, Eve, etc.)

Red Red/System

  • Como Carl Sassenrath encerrou o desenvolvimento de Rebol 2 e disponibilizou os fontes (fora as partes que possuíam copyright) mas largou de mão, muitos ficaram órfãos. O desenvolvimento da versão 3 pela Atronix também data de 2/3 anos atrás. Red pode ser adotado por quem está sem alternativas (e por outros também).
  • Para quem possui um grande leque de aplicações que vão de drives para o SO até aplicativos para rodar no Windows+Mac+Linux+Android (outras opções no futuro como iOS) pode ser uma boa opção.
  • Para quem está de olho na IoT e não quer muita complicação também poderá ser uma opção interessante.
  • Para quem está testando novas linguagens, blá, blá, blá.
Cuidado. O texto abaixo é altamente tendencioso.
O meu interesse por Red é pela descomplicação que ele parece ter (facilidade de passear por diversos SOs sem precisar nem instalar e com 1M), escrevo um programa e gero executável para diversos ambientes, pelo que pode fazer de baixo nível (falar com o Kernel que não é o meu caso) até alto nível (criar DSL ou dialetos como são chamadas em Red), usar GUI nativa dos diversos SOs (atualmente só Windows mas para as próximas versões Android, Mac e Linux/GTK3) e previsões futuras como módulos acopláveis (usar a GPU, por exemplo), meta-DSL (facilidade para a criação de DSLs). Resumindo, parece que os desenvolvedores miraram nas estrelas para alcançar a lua.

Um exemplo que acho legal em Red é o mostrado abaixo. Em poucas linhas é possível criar uma janela, editar um código e ver em tempo real o resultado. Se você tiver curiosidade, basta baixar Red para Windows (é a versão 0.6.2 que está em desenvolvimento e possui o nome de red-latest.exe), baixar o código fonte do relógio que possui 51 linhas contendo o código para criar o ambiente, para criar o relógio e mais 16 linhas que falam sobre o programa. Funciona no wine, bastando tiraro comando transparent da primeira linha. Interessante ler outras informações no site.

blue-clock2

Clock livecoding demo

Vai demorar um bom tempo para Red chegar na versão 1.0. Alguns acham que a versão 0.7 já poderia ser considerada a 1.0 pois possui suporte a parte de I/O. Como não venho de Rebol, olho para o que está considerado para I/O e penso: “Não precisa de tudo aquilo para I/O. Já é possível ler e escrever arquivos, diretórios, posso ler até páginas pelo protocolo http[s]”. Mas se eles dizem …

Red – Parser

Introdução

É possível dizer que o objetivo do Parser no Red é o mesmo das Expressões Regulares (ER) em outras linguagens de programação. As diferenças básicas são:

  • Criado como uma dialeto (DSL) em Red.
  • Mais legível que uma ER (depende?).
  • Por ser criado em Red, possui uma grande integração com a linguagem.

O objetivo é, dada uma entrada qualquer (em Red é uma série como string, lista, etc.) e, baseando-se em determinadas regras, iremos verificar as ocorrências que casam na entrada para tomarmos diversas decisões. Em Red, podemos apenas verificar o casamento das regras retornando falso ou verdadeiro, coletar as partes onde a regra casa ou alterar os trechos onde as regras casam, etc..

Vejamos um caso prático e simples:

Desejamos verificar se o formato de um CPF é válido. Iremos aceitar valores no formato de três séries de três números, separados por ponto, um separador para o controle que poderá ser hífen ou barra e uma sequência de dois número para o controle. Ficaria algo como 999.999.999[-/]99. Uma das opções para o Parse em Red seria:

digitos: charset "0123456789"
centena: [3 digitos]
dezena: [2 digitos]
separador: charset "/-"
cpf: [centena dot centena dot centena separador dezena]

Diferente das ERs, Red não vem com a definição de dados numéricos (\d) para o Parser. Mas, como pode ser visto, é simples de definir. Também definimos centena como uma sequência de três dígitos e dezena como uma sequência de dois dígitos para facilitar a leitura da regra. Definimos os dois caracteres para o separador. O charset especifica que qualquer um dos caracteres irá casar com a regra. Poderíamos definir como [“/” | “-“], isto é, barra ou hífen. Se definíssemos apenas como “/-“, para casar, os dois caracteres deveriam estar presentes na ordem indicada. Alguns caracteres como dot, newline, cr, etc. já estão definidos na linguagem. Optei por usar o dot em vez de “.” para facilitar a leitura da regra. Finalmente criei a regra cpf (que é composta por várias outras regras) para validar se uma entrada é um CPF válido ou não (não estou calculando o controle, apenas o formato da entrada).

Se eu estiver no REPL do Red, uma sessão típica poderia ser:

red>> parse "100.010.001-00" cpf
== true

red>> parse "000.000.000/00" cpf
== true

red>> parse "999.111.555/77" cpf
== true

red>> parse "192.168.45" cpf
== false

red>> parse "192.168.45-77" cpf
== false

Outra possibilidade para a utilização da regra CPF seria para extrair todos os CPFs de um texto. Supondo o seguinte texto:

NOME: João
IP: 192.45.77.44
CPF: 111.111.111/11
NOME: Antônio
IP: 194.58.58.9
CPF: 123.456.789-00
NOME: Empresa Ltda.
IP: 201.195.45.44
CGC: 44.555.666/0001-11

No REPL e supondo-se que o texto estivesse sendo referenciado por s, teríamos algo como:

red>> parse s [collect some [keep cpf | skip]]
== ["111.111.111/11" "123.456.789-00"]

Aqui o collect irá colocar os CPFs da entrada em uma lista e o keep informa o que deverá ser armazenado na lista. No caso, keep cpf ou salta para a próxima posição na entrada.

Entre as outras utilizações para a regra, a possibilidade de padronizar o formato do CPF tipo trocar a barra pelo hífen.

Como falei antes “integrado com a linguagem Red”, é possível passar um código para a regra e ele será interpretado quando a regra é satisfeita. Por exemplo (considerando o texto acima):

parse s [some [copy v cpf (print ["Encontrei:" v]) | skip ]
Encontrei: 111.111.111/11
Encontrei: 123.456.789-00
== true

Quando a regra casar com a entrada, v conterá uma referência ao valor encontrado e o código que estiver entre parênteses será executado.

Sei que você não vai dizer, mas caso diga algo como: “Ah mas com ER eu escrevo menos. Seria algo como \d{3}\.\d{3}\.\d{3}-\d{2}”

Se o problema fosse só quantos caracteres você digita, certamente você iria programar em J ou K. Se você não programa nessas linguagens, você não está sendo 100% sincero. O ideal seria a gente pensar, pressionar enter e …. o programa se materializar no disco. Bem, também é possível diminuir a verbosidade no Red. Poderia ter algo como:

D: charset "0123456789"</pre>
<pre>cpf: [3 D dot 3 D dot 3 D "-" 2 D]

Não ficou tão ruim né? Pessoalmente, até acho um pouco mais legível que a ER para um leigo.

Por curiosidade, se você quiser mostrar o título de uma página html, o código ficaria assim:

page: read http://www.red-lang.org/
parse page [thru &lt;title&gt; copy titulo to &lt;/title&gt;]
print titulo

e será apresentado “Red Programming Language”.

Recursividade

As regras podem ser recursivas. Se você deseja validar se uma entrada é uma expressão matemática válida, você pode aplicar algo como o código abaixo. Pense em algo como BNF.

expr:    [term ["+" | "-"] expr | term]
term:    [factor ["*" | "/"] term | factor]
factor:  [primary "**" factor | primary]
primary: [some digit | "(" expr ")"]
digit:   charset "0123456789"

Para as regras acima você usou, basicamente, apenas o que já foi mostrado neste artigo. O único acréscimo foi de some que significa 1 ou mais ocorrências, isto é, no mínimo um dígito. Tem o any que significa zero ou mais. Se você conhece ER irá associar imediatamente os dois meta-caracteres para as duas funções.

Então parse “1+2*(3-2)/4” expr irá retornar true (é uma expressão válida para a regra) ao passo que parse “1-(3/)+2” expr irá retornar false (a expressão é inválida conforme a regra).

Então é isso. Ficar por aqui para não escrever um livro (a única forma de abranger detalhadamente tudo).

 

Programação Reativa com Red.

Fora as diversas correções e novas características da versão 0.6.1 de Red, a que me chamou mais a atenção foi a programação reativa. A maioria que já desenvolveu algum programa para alguma GUI já teve contato com a programação por eventos, isto é, quando acontece algum evento com a alteração de um texto, clicar um botão, etc., é gerado um evento que será tratado. Cada elemento da janela poderá estar associado a um trecho de código quando ocorrer algum evento relacionado a ele.

Na programação reativa, ocorre uma visão inversa. Algum trecho de código está associado a um ou mais eventos que acontecem. A implementação em Red contempla não apenas GUI como também código. Um exemplo simples seria o somatório de uma região em uma planilha. Se você especificou que o valor de uma célula é o somatório de uma parte de uma coluna como A1..A10, por exemplo, sempre que ocorrer alguma alteração nos valores de alguma das células relacionadas com o somatório, o cálculo deverá ser refeito. Outro exemplo poderia misturar software com hardware e teríamos um alarme que, quando acionado, muda o estado de uma variável e alguma ação deverá ser realizada. O evento (alarme alterando a variável) gera uma reação do programa (é executado um treco de código).

O destaque fica por conta de como foi feita a implementação. Foi criado um framework em Red com aproximadamente 250 linhas de código (por enquanto) utilizando o tipo objeto. O código está no github.

Por enquanto, a implementação pode ser considerada experimental mas já é possível testar algumas funcionalidades com ela. Abaixo você pode ver um exemplo simples de um objeto com as variáveis x e que, quando alteradas recalculam automaticamente a variável total que nada mais é que a soma de x e y. Como também pode ser utilizado em uma GUI, no exemplo abaixo, poderíamos ter uma janela com os campos x e y como sendo para a entrada dos valores e, em vez de direcionarmos o evento de cada uma para a soma e atualização do total, podemos apenas informar a widget do total reagir de forma adequada quando os valores forem alterados.

Por enquanto era isto. Agora é aguardar a versão 0.6.3 que permitira o desenvolvimento de aplicativos para Android. A versão 0.6.2 é a preparação para que os programas possam rodar no Android. É claro que teremos melhorias e correções enquanto isso.

Você poderá obter mais informações sobre a programação reativa bem como diversos exemplos na página de Red.

 

Red -convertendo imagens para tons de cinza

Resolvi fazer um programa em Red (lembrando que ainda está em desenvolvimento e na versão 0.6) para converter uma imagem colorida em preto e branco (tonalidades de cinza). Basta que as cores R, G e B possuam o mesmo valor para que a imagem seja mostrada com tonalidades de cinza. Basicamente temos seis formas de conversão, cada qual apresentando resultados característicos.

Claridade

O nível do cinza será calculado como
Claridade = (max(R, G, B) + min(R, G, B)) / 2

Luminosidade

O nível do cinza será calculado como
Luminosidade = 0.21 × R + 0.72 × G + 0.07 × B

Brilho Médio

O nível do cinza será calculado como
Brilho médio = (R + G + B) / 3

Canais

Escolhemos um dos canais (R, G, B) e replicamos e seu valor nos outros dois canais. Esta técnica simula uma foto feita com um filme preto e branco com a utilização de um filtro com uma das cores dos canais. O resultado é que as cores complementares serão bloqueadas deixando passar apenas a cor do filtro. Utilizando um filtro vermelho em uma foto onde aparece o céu azul e com nuvens brancas, o céu ficará bem mais escuro entregando uma maior dramaticidade para a foto.

O efeito obtido por cada uma das técnicas pode ser visto nas imagens abaixo.
Como a parte gráfica para Mac e Linux ainda estão em desenvolvimento (a do Windows também mas está mais próxima do resultado final), o programa só roda no Windows. Futuramente deverá rodar no Mac, Linux, etc..

Casualmente o programa ficou com 100 linhas como pode ser visto abaixo.

Resultado das diferentes conversões para visualizar a diferença entre elas:

O programa ainda sofrerá alterações mas, por enquanto, era isto.