Há pouco mais de um ano, quando eu estava cursando a cadeira de Linguagens de Programação Funcionais, tive o prazer de começar a utilizar interfaces gráficas em Haskell, linguagem utilizada pela disciplina. Dentre os vários toolkits gráficos disponíveis, o que nos foi apresentado foi o wxHaskell, na verdade um binding para o kit gráfico multi-plataforma/linguagem wxWidgets.
Tivemos vários problemas com o desenvolvimento:
- o desenvolvimento gráfico para esse binding era exclusivamente textual, logo não há a possibilidade de modelar visualmente(pelo menos que nós conhecêssemos);
- nossos prazos eram curtos, dado que a parte gráfica só foi nos ensinada no final da disciplina;
- boa parte do que tentávamos fazer levava um bom tempo para ser estudado e descoberto como ser feito;
- nossa equipe tinha poucas possibilidades de reunião, se resumindo a através de chat, geralmente;
Mas isso não é o foco deste post. No final das contas, o projeto foi feito.

wxHaskell
Enquanto produzia, para prototipar o que eu poderia usar do toolkit, comecei a fazer brincadeiras. Uma delas seria fazer algo que poderia se tornar um jogo: movimentar uma imagem na tela. A imagem poderia ser um personagem, em um hipotético jogo. =)
Alguns passos fazem-se necessários. É necessário o GHC instalado, e o a biblioteca wxHaskell. Utilizei-nos no Windows, e tive inclusive que utilizar uma versão desatualizada do GHC para tornar possível a execução. Nunca mais o executei, e futuramente posso colocar uma descrição detalhada de como instalar tudo necessário. =)
Como foi feito? Explicando em alguns passos:
1) Importação dos módulos necessários:
import Graphics.UI.Wx
import Graphics.UI.Wxcore
2) Define-se um jogador, o qual encapsula a imagem a ser impressa e uma posição(um ponto na tela):
data Player = Player FilePath Point String
O tipo FilePath é um apelido para o tipo String, não se diferencia em nada de um pedaço de texto, representando um caminho nos arquivos para algo, que no nosso caso será a imagem. Point é um TAD que encapsula dois números – Integers, se não me engano – e faz parte da biblioteca do wx mesmo. A String final é apenas uma representação do nome do jogador. =)
3) Define-se a função principal, a que executará o programa através da interface gráfica. Aqui a chamaremos de gui:
gui = do
esta função é um Monad, ou seja, ela encapsula comportamento imperativo no paradigma funcional da linguagem. A partir da diretiva do o comportamento será similar a código imperativo, onde as chamadas a funções estarão separadas por quebras de linhas, de maneira ordenada. Claro, permanece a necessidade de organização de código através de indentação. Prosseguindo:
gui = do
jog <- varCreate (Player "testFace.gif" (Point 0 0) "joao")
j <- frameFixed [
clientSize := Size 300 200,
text := "teste jogo",
picture := "face.ico",
on paint := (\dc rect -> do
x <- varGet jog
printPlayer x dc
return ()
)
]
Temos no código acima a criação de uma variável, jog, a qual representará um jogador(o tipo abstrato Player, mostrado acima). Passamos na criação a String “testFace.gif”, que é o nome do arquivo que coloquei na mesma pasta que o programa. O ponto passado como ponto inicial foi (Point 0 0), o ponto mínimo de exibição.

testeFace.gif!
Na linha seguinte, definimos
j, o qual será um Frame de tamanho fixo, com o uso da função
frameFixed, onde passamos a lista de argumentos. Widgets em wxHaskell seguem geralmente esta forma, uma função relacionada que recebe uma lista de argumentos, onde outros podem ser configurados posteriormente. No caso, definimos o tamanho utilizável da janela em
clientSize(com o TAD
Size, que funciona recebendo dimensões de largura e altura), o texto da barra da janela em
text, o ícone da janela em
picture, a função que será chamada no momento da “pintura” do widget na tela, em
on paint. A função, que declaramos
on the fly através de uma expressão lambda, recebe um contexto de dispositivo(
dc – device context), que neste caso é a janela, e um retângulo(
rect), que especifica as coordenadas da área de visualização. Passaremos o
dc como segundo argumento para a função de impressão, printPlayer. O primeiro argumento que passamos é o Player da variável
jog, através da constante x, inicializada em:
x <- varGet jog
A função printPlayer pode ser descrita no código como:
printPlayer :: Player->DC a->IO ()
printPlayer (Player a b c) dc = do
im <- bitmapCreateFromFile a
drawBitmap dc im b True []
ou seja, recebe o Player e o Device Context, e utiliza a função de criação de bitmap a partir de um caminho, bitmapCreateFromFile, e a função de desenho de bitmaps, drawBitmap, ambas últimas da própria API de WxHaskell. Temos a criação do bitmap im, que é impresso no dispositivo dc, nas cordenadas indicadas pelo ponto b, com o modo de transparência especificado(True). A lista vazia que está como último argumento são as propriedades, as quais não cheguei a utilizar(e descobrir utilidade).
Continuando no código da função gui, após a criação do FrameFixed j, configuramos alguns eventos para ele. Acredito que seja um padrão de codificação configurar os eventos fora da lista principal de atributos. Escolhi só deixar o on paint por ter caráter intrínsico ao frame, diferente dos que criaremos agora:
set j [
on downKey := (do
varUpdate jog desce
repaint j
return ();
),
on upKey := (do
varUpdate jog sobe
repaint j
return ();
),
on rightKey := (do
varUpdate jog direita
repaint j
return ();
),
on leftKey := (do
varUpdate jog esquerda
repaint j
return ();
)
]
os eventos configurados acima servem para movimentar a imagem a partir das setas do teclado. WxHaskell já utiliza eventos para as 4 teclas direcionais, listadas acima(ex: on rightKey, onde rightKey é o evento de apertar a seta para a direita). O que acontece a cada evento: é chamada a função varUpdate, da própria API de WxHaskell, a qual é uma função polimórfica. Se não me engano, sua assinatura é a seguinte:
varUpdate::a->(a->a)->IO ()
ou seja, recebe um argumento do tipo a, e uma função que também recebe um argumento deste tipo e retorna outro do tipo a, e finalmente retorna um IO (). Após a chamada de varUpdate, temos a chamada da função repaint, tomando como argumento o frameFixed j. O que esta função faz é disparar o evento de pintura(paint) do argumento, já descrito acima.
Mas o que as funções passadas como argumento para varUpdate – esquerda, direita, sobe e desce – fazem? Vejamos uma delas:
sobe :: Player->Player
sobe (Player a (Point x y) c) = if y==0 then (Player a (Point x y) c)
else Player a (Point x (y-k)) c
sobe recebe um argumento do tipo Player e retorna o mesmo tipo. A lógica desta função consiste em checar se a posição do jogador já atingiu o topo da janela(quando y é igual a 0), e caso contrário retornar um Player com o valor de y reduzido em k, onde k é uma constante de movimentação, indicando quantos pixels serão saltados. O espaço percorrido por unidade de tempo – ou seja, por disparamento do evento – definirá a velocidade do personagem, sendo seus valores diretamente proporcionais. No meu caso, utilizei:
k::Int --Constante de movimentação
k = 10
As outras funções seguem lógica idêntica, sendo somente necessário checar por colisão do Player com as bordas de j, por isso não as colocarei por extenso aqui. Portanto, a partir de cada evento configurado (o pressionar das setas direcionais) temos uma atualização configurada para a variável jog, e portanto a imagem se comportará da maneira apropriada. Poderíamos brincar um pouco com isso também, fazendo alterações de imagens a partir de determinadas situações, que poderiam ser cláusulas if-then-else nas funções de atualização. Basta usar a criatividade.
Após isso, basta compilar o código e voilá!, você tem uma imagem se movimentando pela sua tela. =)
Comentários de aperfeiçoamentos e hacks a partir disto são bem-vindos! Breve, quem sabe, coloco aqui algo com GTK2Haskell!