terça-feira, 22 de dezembro de 2009

Seaside

Squeak Seaside é um arcabouço (framework) para desenvolvimento de aplicações web para Smalltalk. É descrito pelo próprio autor como um arcabouço herético, por quebrar todas as regras dos gurudons atuais.

Pessoalmente gosto muito de coisas heréticas ou subversivas. Sou um apaixonado por subversão. =D

Olhando a documentação, podemos ver que Seaside é suportado pelas máquinas virtuais Cincom VisualWorks, GNU Smalltalk, VA Smalltalk, Squeak e seus derivados Pharo e GLASS.

GNU Smalltalk


A máquina virtual recomentada é Squeak, da qual gosto muito e que é ótima para aplicações desktop, no entanto Squeak é uma plataforma muito pesada, consome muitos recursos da máquina, por isso, para Seaside, optei por uma plataforma mais leve: GNU Smalltalk.

GNU Smalltalk sofre porém de um mal extremamente irritante: a sintaxe da linguagem é muito irregular – para ser eufémico.

Teoricamente GNU Smalltalk suportaria a sintaxe correta de Smalltalk-80, mas não funciona bem assim… ele é muito chato e levanta erros de sintaxe para códigos perfeitamente corretos, mas que diferem do que o GNU Smalltalk espera de Smalltalk-80. Então é preciso experimentar algumas formas diferentes – mas regulares – de fazer as coisas antes de encontrar uma que funcione (às vezes ele implica até com nomes de argumentos).

Vou usar aqui a sintaxe Smalltalk-80 precisa – e que funciona no GNU Smalltalk –, mas colocarei o mesmo código na sintaxe peculiar do GNU Smalltalk ao final deste artigo apenas por curiosidade.

Preparação da imagem


Para criar a imagem para nosso exemplo usei o comando:
bash$ gst-load -iI seaside.im Seaside Seaside-Development Seaside-Examples


Isso cria uma imagem seaside.im com os pacotes Seaside (Seaside em si), Seaside-Development (ferramentas de desenvolvimento) e Seaside-Examples (exemplos).

Usei então o exemplo de componente da própria documentação: MyCounter.st.
  • O método de classe #canBeRoot diz se o componente pode ser registrado como uma aplicação autónoma ou não.
  • Na inicialização (#initialize) o atributo count é zerado.
  • O método #states retorna uma coleção de estados que podem ser retrilhados (backtracked).
  • O método #renderContentOn: é usado para renderizar a página.
  • Ao final a mensagem #registerAsApplication: registra o componente como aplicação.


O código fica então (se copiar e colar, substituia por ^):
" See: http://www.gnu.org/software/smalltalk/manual/html_node/Seaside.html "

Seaside.WAComponent subclass: #MyCounter
instanceVariableNames: 'count'
classVariableNames: ''
poolDictionaries: ''
category: 'Seaside Examples'
!

!MyCounter class methodsFor: 'testing'!

canBeRoot
↑true
!

!

!MyCounter methodsFor: 'initializing'!

initialize
super initialize.
count := 0.
!

!

!MyCounter methodsFor: 'accessing'!

states
↑{ self }
!

!

!MyCounter methodsFor: 'rendering'!

renderContentOn: html
html heading: count.
html anchor
callback: [ count := count + 1 ];
with: '++'.
html space.
html anchor
callback: [ count := count - 1 ];
with: '--'.
!

!

MyCounter registerAsApplication: 'mycounter'.


Para carregar o componente na imagem usei o comando:
bash$ gst -I seaside.im -S MyCounter.st


Rodar a aplicação


O seguinte comando levanta a máquina virtual no modo servidor:
bash$ gst-remote -I seaside.im --daemon


Depois disso você poderá ver a máquina virtual ouvindo na porta 12345/TCP:
bash$ netstat -ln4 | grep 12345
tcp 0 0 0.0.0.0:12345 0.0.0.0:* LISTEN


É possível obter o PID do processo da máquina virtual com seguinte comando:
bash$ gst-remote --pid


Porém o Seaside não está rodando. Para rodar o serviço web use o comando:
bash$ gst-remote --start=Seaside


É possível também iniciar a máquina virtual já rodando o Seaside com o seguinte comando:
bash$ gst-remote -I seaside.im --daemon --start=Seaside


Por padrão o serviço web Seaside estará escutando na porta 8080/TCP.

Para acessar a aplicação que acabamos de criar use o endereço http://localhost:8080/seaside/mycounter.

Parar a aplicação


Para parar a aplicação use o comando:
bash$ gst-remote --stop=Seaside


Repare que, apesar do serviço web ter parado, a máquina virtual continua rodando. Para pará-la:
bash$ gst-remote --kill


Para comparação


A título de comparação, segue o código usando a sintaxe bizarra e esdrúxula peculiar do GNU Smalltalk:
Seaside.WAComponent subclass: MyCounter [
| count |

<category: 'Seaside Examples'>
<comment: nil>

MyCounter class >> canBeRoot [
<category: 'testing'>
^true
]


initialize [
<category: 'initializing'>
super initialize.
count := 0
]

states [
<category: 'accessing'>
^{ self }
]

renderContentOn: html [
<category: 'rendering'>
html heading: count.
(html anchor)
callback: [count := count + 1];
with: '++'.
html space.
(html anchor)
callback: [count := count - 1];
with: '--'
]
].

MyCounter registerAsApplication: 'mycounter'.


[]'s
Cacilhas, La Batalema

domingo, 6 de dezembro de 2009

Mais sobre reiteração

Glider A reiteração ou iteração é um dos mais efetivos algoritmos para para processamento de sequências, mas sua eficiência não se limita a processamento de conjuntos prontos, essa técnica de algoritmo também pode ser usada para processamento de amostragens em plena coleta.

Vamos a um algoritmo bem simples, a média aritmética:
mean_x=sum_x/n


O algoritmo reiterativo pode ser expresso da seguinte forma:
(defun mean ((a-list list))
(/
(apply #'+ a-list)
(length a-list)))


Agora imagine que você tem o registro contínuo do comportamento de um dado específico ao longo do tempo e gostaria de manter sua média sem preocupação como os valores em si (acho um caso pouco provável, mas…).

Se reparando bem, há dois dados importantes para o cálculo da média aritmética: a própria média e a quantidade de elementos. Basta que a função receba esses valores além dos novos valores que serão usados para atualizar a média anterior.

Assim é possível armazenar apenas a média atual e a quantidade de elementos avaliados:
(defun update-mean ((the-mean hash-table) (a-list list))
(let ((*count* (+ (gethash 'count the-mean) (length a-list))))
(setf (gethash 'mean the-mean)
(/
(+
(apply #'+ a-list)
(* (gethash 'mean the-mean) (gethash 'count the-mean)))
*count*))
(setf (gethash 'count the-mean) *count*)
the-mean))


Então, além de receber a lista com os dados mais recentes (segundo parâmetro), a função recebe como primeiro parâmetro um hash com os dados de média anteriores.

O formato inicial do hash deve ser:
#s(hash-table :test fasthash-eql (count . 0) (mean . 0))


Ou seja, 'mean zero (0) e 'count zero (0). O hash será atualizado e retornado:
(setq *mean*
(make-hash-table
:initial-contents (list
(cons 'count 0)
(cons 'mean 0))))


Na próxima falarei em variância on-line.

[]'s
Cacilhas, La Batalema