Codemountain, Paulo Suzart's Blog

Posts Tagged ‘programming

Flowchart de apoio a escolha de tipos em Clojure

with 4 comments

Muitas vezes fiquei em dúvida sobre qual forma de definição de tipos em Clojure utilizar. As opçõe são muitas: deftype, defrecord, reify, proxy, e gen-class e é fundamental um bom entendimento de cada uma delas para fazer a melhor escolha sua implementação.

Foi aí que encontrei este post de Chas Emerick, um dos autores de Clojure Programming: Rough Cuts Version. Este livro promete muito e não vejo a hora de tê-lo em mãos.

No post, Chas fala a respeito da tarefa de escolher o tipo certo e mostra um fluxo que ajuda e muito, não só entender os tipos, mas como fazer a melhor escolha. Depois de OGINO Masanori oferecer uma versão em Japonês, me ofereci para fazer uma tradução para português, e Chas aceitou. Assim você pode usar como Wall paper ou fazer impressão bem grande para deixar na parede mais próxima. Clique na imagem para ampliar.

Usando as próprias palavras do autor, a Zona de Interoperabilidade (ou “The ‘Interop Zone'”) do chart, agrega os seguintes casos:

“The ‘Interop Zone’” demarcates use cases (e.g. needing to define multiple constructors) and forms (proxy and gen-class) that are exclusively the domain of Clojure’s JVM interoperability support. Using these forms is fine, but be aware that by doing so, you are stepping outside of Clojure’s “native” abstractions; unless you are defining a type for the express purpose of meeting interoperability requirements, one of Clojure’s simpler type-definition forms may suit your needs better. (Chas Emerick)

Eu já precisei usar um misto de Protocols, types e Proxy como você pode ver aqui. Era justamente um caso de interoperabilidade com classe puramente Java, e obtive um ótimo resultado.

Espero que o flowchart seja útil para você também.

Update 2011-11-17 07:46 pm UTC: Chas teve uma grande ideia! Ele criou um repositório no Github onde é possível contribuir com traduções para este chart, que é dos mais úteis que já vi. Ele me acionou pelo twitter e tive o prazer em fazer a versão em Português para colocar no repo. O chart original estará publicado em seu livro Clojure Programming – que sai no final deste ano.

Written by paulosuzart

agosto 22, 2011 at 6:34 pm

Publicado em clojure, coding

Tagged with , ,

Stream Server com Clojure e Aleph

with one comment

Hoje vou ser objetivo e já começo com o código:

(ns NewStream.core
	(:use [aleph.http])
	(:require [lamina.core :as l]
	          [beanstalk.core :as b]))

	(def beans (b/new-beanstalk "192.168.0.105" 11300))

	(defn init
		[ch]
		(l/receive-all ch (fn[_]))
		(future
			(loop [job (b/reserve beans)]
				(l/enqueue ch (:payload job))
				(b/delete beans (:id job))
				(recur (b/reserve beans)))))

	(defn handler
		[ch requet]
		(let [stream (l/named-channel :news-stream init)]
			(l/enqueue ch
				{:status 200
				 :headers {"content-type" "text/html"}
				 :body (l/fork stream)})))

	(defn -main[& args]
		(start-http-server handler {:port 8089}))

E mais objetivo ainda eu digo: O Aleph é simplesmente sensacional! É um framework de comunicação assíncrona feito com o excelente JBoss Netty e um framework muito completo para programações baseada em eventos do mesmo autor do Aleph, o Lamina.

O Lamina usa canais como cidadãos de primeiro nível e compartilha o mesmo princípio de canais em Go. Mas é muuuuuuito mais poderoso.

O código acima nasceu de um desafio lançado para @abraaoisvi, @lucastex, @rafaelfelini e eu (@paulosuzart). O desafio era fazer um servidor de stream de notícias de um dado evento sem usar websocket e algumas outras restrições que não vem ao caso.

Na minha solução, tenho um servidor beanstalkd rodando num linux. Este servidor recebe mensagens de uma aplicação usada por um editor de notícias fictícia (na verdade um código tosco em python meu mesmo). E cada cliente que conecta no Aleph, vai passar então a receber as notícias uma vez emitidas pelo editor.

O código acima tem handler como a função que recebe as requisições de um browser, por exemplo. Veja que nela, stream é um canal que recebe todas as mensagens vindas do beanstalkd. O detalhe é:news-stream que é o nome dado ao canal de mensagens. Isto é, existirá apenas um canal com este nome no servidor.

init é a função que inicializa o canal stream (recebido como parâmetro ch). Como a função reserve da lib beanstalk é blocking, executo ela em outra thread usando future, e a cada nova mensagem recebida, faço o enqueue no canal stream.

O http handler responde imediatamente para o Browser com status 200 OK, mas o body é um channel que sofreu um fork do canal stream. O fork permite que cada cliente receba uma cópia das mensagens de stream a partir do momento em que fez o fork. O Lamina se encarrega de repassar todas as mensages de stream para os canais gerados a partir do fork.

Com um body sendo um channel, o Aleph vai responder a requisição usando uma resposta Chunked.

Ao acessar http://localhost:8089, o Browser vai manter a conexão aberta. Cada nova mensagem emitida será então enviada imediatamente para cada browser pendurado no server. Simples assim 🙂

Aleph + Lamina, ta aí uma boa combinação para tirar o máximo do seus processadores! Até a próxima.

Update: Algumas pessoas me perguntaram como rodar este pequeno programa. Você pode usar o Leiningen com o seguinte project.clj:


(defproject NewStream "1.0.0-SNAPSHOT"
  :description "Simple news stream server"
  :dependencies [[org.clojure/clojure "1.2.1"]
                 [lamina "0.4.0-beta2-SNAPSHOT"]
                 [aleph "0.2.0-beta2-SNAPSHOT"]
                 [org.clojars.bengl3rt/beanstalk "1.0.2"]]
  :main NewStream.core)

Depois é só executar lein deps e então lein run. Ah, não esquece de ter um Servidor beanstalkd rodando.

Written by paulosuzart

agosto 15, 2011 at 12:19 am

Publicado em clojure

Tagged with ,

Golang – Templates e formatters

leave a comment »

Adivinhou! Irei escrever: “Quanto tempo sem postar no blog!” É isso mesmo. Quanto tempo!

Se você tem acompanhado meus tweets, deve ter notado que estou focado em Go. Bom, na verdade não estou twittando ou postando no blog sobre Go como fiz com Scala e Clojure por alguns motivos. Mas é proposital.

Não aguentei me segurar e segue uma dica sobre templates em Go. Existem outras soluções, até mesmo mais simples, para o problema. Mas resolvi fazer dessa forma para não interferir nos dados da aplicação por questão de apresentação e visualização de dados.

var CustomFormatter = template.FormatterMap{
    "f2mi": func(w io.Writer, format string, value ...interface{}) {
                 fmt.Fprint(w, strconv.Ftoa64(value[0].(float64)/1000000, 'f', -1))
    },
    "i2mi": func(w io.Writer, format string, value ...interface{}) {
        fmt.Fprintf(w, strconv.Ftoa64(float64(value[0].(int64))/1000000, 'f', -1))
    },
}

var OutPutTemplate = `
=========================================================================
Test Summary (gb. Version: 0.0.2 alpha)
-------------------------------------------------------------------------
Total Go Benchmark Time         | {Elapsed|i2mi} milisecs
Requests Performed              | {TotalSuc}
Requests Lost                   | {TotalErr}
Average Response Time           | {Avg|f2mi} milisecs
Max Response Time               | {Max|i2mi} milisecs
Min Response Time               | {Min|i2mi} milisecs
`
//The resunting summary of a master
type Summary struct {
    Start, End         int64
    TotalSuc, TotalErr int
    Min, Max           int64
    Avg                float64
    Elapsed            int64
}

func (self *Summary) String() string {
    t := template.MustParse(OutPutTemplate, CustomFormatter)
    sw := new(StringWritter)
    t.Execute(sw, self)
    return sw.s
}

Wow! O que está acontecendo aqui? Bem, começando pela variável OutPutTemplate, temos o template usado no GoBenchmark como saída no final do teste.

Note que entre as chaves ({}) estão exatamente os campos da struct Summary, definida mais abaixo. a função (self *Summary) String() em Go, é o correspondente do método .toString() em Java. Ok? Acontece que os campos com in64 estão guardando valores em nanosegundos. Mas para o usuário final desejo mostrar em milisegundos.

E por que não dividir cada campo por 10 elevada à 6 potência? Realmente daria, mas precisaria cuidar disso no meu código da aplicação, e não quero isso.

Uma solução é combinar ao template alguns formatadores de valor. Por isso a média (campo Avg) é representada no template por {Avg|f2mi}, isto é, {Campo|formatador}. Note que em String(), ao efetuar o parse do template eu uso o CustomFormatter para indicar quais os formatadores usados.

É possível usar reflection para, em um mesmo formatador, decidir se o argumento value é um int64 ou float64. Mas, prefiro deixar reflection pra situações realmente relevantes.

É isso! Os formatadores são boas opções e permite soluções criativas.

Se você não me segue no twitter, aproveite: @paulosuzart. Até a próxima!

Written by paulosuzart

julho 1, 2011 at 7:30 am

Publicado em golang

Tagged with ,

clojure + xml com saxon e xstandard

leave a comment »

Outro dia no trabalho estava definindo alguns padrões de como um XSD deveria ser escrito. Coisas simples como formato de nomes de elements, complexTypes, se o XSD deveria ter targetNamespace e attributeFormDefault.

Foi aí que surgiu a ideia de fazer um script em clojure que validasse isso em um dado XML e indicasse o que está fora destes padrões. Claro, algo assim existe por aí. Mas não dá pra perder a oportunidade. Juntei algumas funções auxiliares em cima do Saxon, um wrapper clojure do Saxon XSLT and XQuery Processor. A ideia foi pegar a base do Saxon e usar de tal forma que eu pudesse aplicar algumas asserções a um documento XML.

Queria trabalhar com a idea de assertions. Elas, além do nome, seriam compostas por uma expressão xPath, uma mensagem de falha, uma segunda expressão xPath para localizar que atributo do nó sendo avaliado seria considerado como display-name e uma função de validação que receberá o nó sendo avaliado e simplesmente retornaria true ou false. O resultado foi uma pequena lib commitada no meu github: xstandard.

Por questão de praticidade, escolhi uma estrutura de map par armazenar as informações de uma assertion. Ah, e pra tornar ainda mais prático, criei uma macro que ajuda a definir uma assertion assim:

(defassertion element-name "//xsd:element[@name]"
  :msg "element %s does not match [a-z].*."
  :validator (attr-matches "name" #"[a-z].*")
  :display-name "data(./@name)")

Esta assertion fará análise – através do validator –  de todo elemento do meu XSD cujo atributo name esteja presente. Ok, mas decidi agrupar as assertions, eu poderia eventualmente tratar grupos diferentes de formas diferentes ou usar  a saída delas de forma diferente. Por isso existe uma outra macro que me permite fazer assim:


(defassertions *default-assertions*

  (defassertion element-name "//xsd:element[@name]"
    :msg "element %s does not match [a-z].*."
    :validator (attr-matches "name" #"[a-z].*")
    :display-name "data(./@name)")

  (defassertion type-name "//xsd:complexType[@name]"
    :msg "type %s does not match [A-Z].*Type."
    :validator (attr-matches "name" #"[A-Z].*Type")
    :display-name "data(./@name)")

  (defassertion element-form-default "/xsd:schema"
    :msg "schema hasn't attr elementFormDefault=\"qualified\""
    :validator (attr-eq "elementFormDefault" "qualified"))

  (defassertion target-ns "/xsd:schema"
    :msg "schema hasn't targetNamespace attr"
    :validator (attr-present "targetNamespace")))

Legal, agora temos um grupo de assertions. Mas como executá-las? Simples, basta passar o conjunto de assertions aqui chamado *default-assertions* para a função run provida pela lib. O que fica mais ou menos assim:

  (xs/run *default-assertions* xs/*nss* xmldoc)
  ;; *nss* is provided by the lib with a default namespace prefixe for xml schema.

Por padrão a lib vai retornar o resultado do processamento em um map contendo o nome de cada assertion, o caminho para o nó analisado no xml, a linha no arquivo:

{:assertion :element-name, :status false, :display-name Item,
 :details {:result-msg element Item does not match [a-z].*.,
           :line 25,
           :path /xs:schema/xs:element[1]/xs:complexType[1]/xs:sequence[1]/xs:element[3]}}

Note no código onde declarei todas as asserções, a função attr-matches sendo usada na assertion type-name. Ela recebe como parâmetro o nome do atributo do nó analisado e uma regex para verificar o formato do atributo. Note também que a função é executada no momento da montagem da assertion, pois ela retorna uma segunda função, que esta sim, receberá o nó analisado e fará a checkagem do formato. O código de attr-matches é:

(defn attr-matches
  "Validates the format of a given node `n` against `regex`."
  [attr regex]
  (fn [n]
    (not (nil? (re-matches regex (get-attr n attr))))))

Lindo, não? Bom, para se divertir mais no código basta acessar meu github.

Pra fechar, durante a criação desse projetinho tive a oportunidade de usar a IntelliJ IDEA. Surpreendente! Muito boa e estável, sem falar do plugin clojure e leiningen muito bons. Ah, pra somar ao aprendizado, utilizei o Marginalia pra gerar a documentação do xcode, veja aqui. Basta salvar o html e ver a doc do projeto de uma forma bem interessante. O Marginalia é um projeto do @fogus.

Não esqueça de me seguir no twitter: @paulosuzart.

Written by paulosuzart

fevereiro 7, 2011 at 12:00 pm

Publicado em clojure

Tagged with ,