Você sabia que é possível criar um site em Haskell? Pois é! Por incrível que pareça, Haskell não é (somente) uma linguagem de programação obscura, repleta de paradigmas e conceitos super abstratos e sem nenhuma aplicação prática, que só é usada por matemáticos e acadêmicos muito loucos.

Essas declarações podem parecer óbvias e até mesmo sem sentido para alguns, mas a grande verdade é que ainda tem muita gente por aí achando que Haskell é uma linguagem “limitada”.

Se você parar pra pensar, a gente vive um período meio nebuloso dentro da computação…

Hoje em dia é muito comum encontrar desenvolvedores que avaliam a utilidade e a relevância das tecnologias usando uma lógica pautada em uma relação que é mais ou menos assim:

Gráfico ironizando uma relação distorcida da utilidade das tecnologias

Obviamente essa é uma curva hipotética e de caráter meramente ilustrativo, então não a leve a sério. Até porque eu tenho fortes motivos para acreditar que o cenário real deve estar mais próximo de uma . :joy: :joy: :joy:

Enfim, o ponto é que a métrica “quão fácil é fazer um site com a tecnologia X?”, que acabou se popularizando com a alta demanda de aplicações Web no mercado, já não pode mais ser ignorada.

Por esse motivo que eu resolvi fazer este tutorial, para que nós possamos desenvolver juntos um site em Haskell e para lhe mostrar que não é necessário um PhD em Ciência da Computação e outro em Matemática para se fazer “coisas úteis” em Haskell.

É claro que, se você já leu algum outro artigo do blog, você já deve saber que esse não será um site qualquer, né? O lance é que aqui na Mavins a gente gosta de coisas com aplicações reais…

É por isso que neste primeiro tutorial nós iremos além do “Hello, world!” moderno, e você me ajudará a criar a estrutura básica de um site de lista de presentes usando o Bootstrap e o Heist. Além disso, você aprenderá como evitar muitas dores de cabeça configurando corretamente o seu ambiente de desenvolvimento Haskell com o Docker e o Stack.

Tá, mas calma aí… Lista de presentes? Onde está a aplicação prática disso?

Bom, talvez você não saiba, mas este ano eu vou me casar (:clap: :clap:), e nada melhor do que contar com aquela ajudinha marota dos convidados disponibilizando uma lista de presentes em um site powered by HaskellTM, não é mesmo?! :laughing: :v:

Então pense neste tutorial como um investimento na sua vida: complete-o que aí só vai ficar faltando a noiva mesmo… :joy: :joy:

E como assim “eu vou te ajudar a criar um site”?

Ahhh, sobre essa pergunta eu farei um pouco de mistério… Você vai ter que ler o artigo até o final (e de olhos bem abertos) para descobrir! :flushed:

A única dica que eu vou dar é: se você gostar desse tipo de assunto, fique ligado no blog e não deixe de se inscrever na nossa lista de emails porque vem muito mais artigos sobre desenvolvimento Web em Haskell por aí. :wink:

Por fim, eu espero que eu consiga com este artigo despertar em você pelo menos um bit de curiosidade sobre essa linguagem fantástica que é Haskell.

Vumbora então?

Vá direto ao assunto…

Configure o ambiente: Docker e Stack

Assim como em Python (leia também: “Python e virtualenv: como programar em ambientes virtuais”), conflitos de dependência e incompatibilidades entre diferentes versões do compilador/linguagem também podem ocorrer em Haskell.

Por isso nós usaremos o Docker e o Stack para desenvolver a nossa aplicação em um ambiente isolado, possibilitando a reprodutibilidade dos builds, facilitando o deploy e evitando aquela máxima: “mas funciona na minha máquina” — que todo programador já ouviu pelo menos uma vez ao trabalhar em times.

O restante desta seção foi dedicado para que você entenda os principais conceitos inerentes a essas tecnologias e para que você aprenda a configurar o Stack para trabalhar junto com o Docker de forma transparente, criando um ambiente de desenvolvimento que possibilite:

  • Avaliar módulos do seu projeto em uma sessão do interpretador GHCi que executa diretamente dentro de um contêiner Docker.
  • Compilar, testar e executar, a partir do host, a sua aplicação dentro de um contêiner Docker.

O porquê do “Docker e Stack”

Em poucas palavras, o Docker possibilita o empacotamento de uma aplicação ou ambiente inteiro dentro de um contêiner, cuja portabilidade pode ser realizada para qualquer servidor que possua o Docker instalado.

O Stack, por sua vez, é um programa para o desenvolvimento de aplicações em Haskell que possui mecanismos de integração com o Docker. Dentre as funcionalidades cobertas pelo Stack, destacam-se:

  • Instalação da versão adequada do compilador GHC
  • Instalação de pacotes de forma consistente
  • Gerenciamento de build
  • Execução de testes
  • Benchmarking

Eu sei que para quem já mexe com Haskell, configurar o Docker e o Stack para um tutorial sobre Heist pode parecer usar uma bazuca para matar uma formiguinha. Eu concordo em parte. Embora isso possa assustar em um primeiro momento (não se assuste :wink:), eu entendo que se é para aprender, então que seja do jeito certo, não é mesmo?

Por isso, aceite o meu conselho: invista o tempo necessário para entender esta seção, pesquise na Internet sobre essas ferramentas (Docker e Stack) e use a seção de comentários ao final do post para tirar as suas dúvidas. Eu garanto que valerá a pena!

Instalação e configuração

Antes de partirmos para os comandos, vai uma última observação: infelizmente o Stack não funciona no Windows (pois é :confused:). Eu até reclamaria disso, se não fosse o fato de que se você usa Windows para programar em Haskell, então o problema é bem maior.

Por outro lado, se você é usuário da Apple, eu apenas recomendo atualizar o seu sistema operacional para o macOS Sierra ou superior. Se você usa Linux, então relaxa porque você está em casa e provavelmente você não terá problemas.

Finalmente, vamos aos comandos…

Comece instalando o Docker no seu sistema operacional (siga as intruções neste link).

Então instale a versão mais recente do Stack com o comando:

1
$ curl -sSL https://get.haskellstack.org/ | sh

Na data em que escrevo, a última versão do Stack é a:

1
2
3
$ stack --version
$ Version 1.3.2, Git revision 3f675146590da4f3edf768b89355f798229da2a5 (4395
commits) x86_64 hpack-0.15.0

Agora vá até o diretório onde você deseja criar o seu projeto e execute:

1
$ stack new gift-list simple

O comando stack new cria dentro da pasta gift-list a estrutura que usaremos de base para o nosso projeto a partir do template simple (digite stack templates para ver os templates disponíveis).

Assim que o comando terminar sua execução, insira as seguintes linhas no início do arquivo stack.yaml:

1
2
3
docker:
    enable: true
    image: haskell

Atente-se para a indentação, pois esse é um arquivo YAML (rima não-intencional :smiley:).

A linha 3 serve para especificar a imagem Docker que deve ser usada para criar o contêiner. A opção image: haskell sinaliza para o Stack que ele deve utilizar a imagem oficial do Haskell. Na verdade, essa linha é opcional. Eu a uso apenas porque a imagem oficial atende aos nossos requisitos e é consideravelmente menor do que a fpco/stack-build, que é a imagem que o Stack usa por padrão.

Agora é só executar os dois últimos comandos para, respectivamente, fazer o download da imagem haskell do DockerHub e executar a aplicação gift-list dentro do container:

1
2
$ stack docker pull
$ stack build && stack exec gift-list  

Ao final, você deve ver um clássico “hello world” de saída.

OBS.: Se você trabalhar atrás de um proxy corporativo e o Stack não estiver fazendo o download das dependências, pede ajuda nos comentários que eu já passei por isso. :wink:

Instalando as dependências e configurando o projeto

Só precisamos resolver mais 2 detalhes antes de continuarmos:

  1. Instalar o Heist
  2. Instalar os “pacotes auxiliares”

Instalando o Heist

Abra o arquivo gift-list.cabal e adicione a string heist logo abaixo de base na seção build-depends. Use espaços (não tabs) para alinhar as duas strings e não altere as versões do seu base:

1
2
build-depends:       base >= 4.7 && < 5
                   , heist

Para instalar o Heist e todas as suas dependências, basta invocar uma sessão do GHCi:

1
$ stack ghci

Lembre-se que se tudo estiver configurado certinho, essa sessão deve ser executada transparentemente dentro de um contêiner Docker.

IMPORTANTE: se ocorrer um erro na execução do comando acima, tente executar stack solver --update-config e tente novamente. Para fins deste tutorial, isso deve ser suficiente.

Para confirmar que a instalação foi concluída com sucesso, tente carregar os seguintes módulos do Heist no GHCi com o comando:

1
*Main>:m + Heist Heist.Intepreted

Se os módulos forem carregados corretamente, então você pode sair do GHCi (:q).

Instalando os “pacotes auxiliares”

Esses “pacotes auxiliares” são, na verdade, bibliotecas que nos ajudarão a escrever código Haskell nas próximas seções. A ideia de instalarmos esses pacotes logo agora é podermos nos concentrar apenas nos detalhes de implementação lá na frente.

O procedimento para instalar as demais dependências é similar ao da instalação do Heist descrita anteriormente.

Assim, atualize o build-depends do arquivo gift-list.cabal com os seguintes pacotes:

1
2
3
4
5
6
7
8
 build-depends:       base >= 4.7 && < 5
                    , blaze-builder
                    , blaze-html
                    , heist
                    , lens
                    , map-syntax
                    , text
                    , xmlhtml

Por fim, aproveite que você está com o arquivo gift-list.cabal aberto e edite as suas informações pessoais e as informações do projeto no topo do arquivo (só para ficar “bonitinho” :stuck_out_tongue_winking_eye:).

Construindo o site inicial com Bootstrap

Eu adaptei um template do StartBootstrap para servir de base para o site que nós construiremos neste tutorial.

Nosso ponto de partida será o arquivo index.html, que você pode visualizar clicando aqui. Você pode fazer o download desse arquivo e dos demais assets clicando aqui.

Nós trabalharemos em cima desse arquivo de forma incremental, explicando os principais conceitos do Heist e adicionando novas funcionalidades ao longo das seções que se seguem.

Resumidamente, nas próximas seções você aprenderá a:

  1. Fragmentar a index.html em múltiplos arquivos .tpl (templates) que serão processados pelo Heist para compor o site final
  2. Escrever o código Haskell para renderizar uma nova index.html a partir dos templates e salvá-la na pasta _site/
  3. Gerar uma nova página: sobre.html — referentes aos links no menu principal
  4. Programar em Haskell um splice simples para inserir dinamicamente uma mensagem no site

Eu também gravei uma vídeo-aula com o conteúdo que vem a partir daqui.

Eu procurei deixar bem explicadinho tudo o que eu estava fazendo e, por isso, o vídeo ficou um pouco longo. Mesmo assim, eu recomendo que você assista (põe no 2x, qualquer coisa :grin:) porque ver eu fazer e ouvir as explicações ao mesmo tempo pode te ajudar a entender melhor.

OBS.: Antes de apertar o Play, clique aqui para fazer o download do código-fonte que eu uso na vídeo-aula.

Heist em 60 segundos

Finalmente, com o ambiente todo configuradinho, vamos ao Heist.

E nada melhor do que entender alguma coisa pelas palavras do próprio criador, não é mesmo?

A síntese a seguir é uma adaptação para PT-BR da excelente explicação do Doug Beardsley — o criador do Heist.

Um sistema de templates é uma ponte que conecta dados estáticos (templates) e dados dinâmicos.

O Heist é um sistema de templates que conecta templates HTML e código Haskell.

Um splice é código Haskell que o Heist associa a uma tag HTML.

Toda vez que uma determinada tag aparece em um template, o Heist executa o splice associado a essa tag passando a própria tag (incluindo os seus atributos e suas tags filhas ou tags aninhadas) como entrada/argumento para o splice.

A saída de um splice é uma lista de tags que são substituídas no template no lugar da tag original de entrada.

Splices podem ser entendidos como funções que podem ser chamadas a partir dos templates para se obter dados dinâmicos.

Splices podem passar esses dados dinâmicos de volta para os templates associando (temporariamente) novos splices.

<bind> é uma tag que pode ser usada nos templates para criar novos splices.

<apply> é uma tag que permite a inserção de um template em outro.

Ao invés de expandir cada um desses tópicos e te encher de teoria, eu prefiro que você entenda a dinâmica do Heist na prática. Portanto, use essa seção como referência porque na próxima nós já vamos meter a mão na massa!

Fragmentando a página inicial e entendendo a <apply>

Uma das funcionalidades básicas de um sistema de templates é permitir o reúso de fragmentos de código na hora de construir as páginas.

Geralmente, esses fragmentos são separados em diferentes arquivos e o próprio sistema de templates fornece mecanismos para que você possa montar as páginas como se fosse um quebra-cabeças.

No caso do Heist, essa funcionalidade está implementada em uma tag especial chamada <apply>.

Antes de mostrar como a <apply> funciona, vamos só deixar o nosso projeto mais organizado criando duas novas pastas:

  • templates/: onde armazenaremos os templates (extensão .tpl) que serão processados pelo Heist.
  • site/: onde armazenaremos o nosso site, isto é, os arquivos HTML de saída gerados pelo Heist após o processamento dos templates.

IMPORTANTE: Não esqueça de copiar também a pasta assets/ para dentro do diretório site/ se você estiver visualizando as páginas HTML geradas clicando diretamente sobre elas (sem utilizador um servidor Web), senão vários elementos não serão renderizados.

Agora sim estamos prontos para a <apply>

Vamos criar 5 arquivos na pasta templates/:

  • _head.tpl
  • _nav.tpl
  • _footer.tpl
  • _default.tpl
  • index.tpl

A ideia é simples: no arquivo _head.tpl nós copiamos todo o conteúdo da tag <head> do arquivo index.html de base. No arquivo _nav.tpl, copiamos todo o conteúdo da tag <nav>. E no arquivo _footer.tpl, copiamos todo o conteúdo da tag <footer>.

Dessa forma, o conteúdo desses 3 arquivos poderão ser reusados para construir as demais páginas do nosso site. Por exemplo, assim como a index.html, a página sobre.html também precisará de um cabeçalho, de um menu de navegação e de um rodapé.

Ainda podemos ir além. Como a diferença básica entre essas 2 páginas é o conteúdo do <body>, nós podemos definir um template _default.tpl para descrever uma estrutura padrão para ser usada pelas diferentes páginas que compõem o nosso site:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<!-- default.tpl -->
<!DOCTYPE html>
<html lang="pt-br">

    <!-- Cabeçalho -->
    <apply template="_head">
        <apply-content/>
    </apply>

    <body>

        <!-- Navegação -->
        <apply template="_nav">
            <apply-content/>
        </apply>

        <!-- Conteúdo da página -->
        <div class="container">
            <apply-content/>
        </div>
        <!-- /.container -->

        <!-- Rodapé -->
        <div class="container">
            <hr>
            <apply template="_footer">
                <apply-content/>
            </apply>
        </div>
        <!-- /.container -->

        <!-- jQuery -->
        <script src="assets/js/jquery.js"></script>
        <!-- Bootstrap Core JavaScript -->
        <script src="assets/js/bootstrap.min.js"></script>

    </body>

</html>

Tire um tempo para encarar esse código e veja se você consegue entender intuitivamente o que está acontecendo…

Em poucas palavras, nós definimos a estrutura básica de um página HTML e usamos a tag <apply> para preencher as seções principais da página usando código definido em outros arquivos.

Por exemplo, nas linhas 5–7, nós selecionamos o template _head.tpl e “aplicamos” o seu conteúdo com a tag <apply-content/>. Nós fazemos o mesmo para os demais templates: _nav.tpl e _footer.tpl. Observe que a extensão .tpl deve ser omitida no atributo template da tag <apply>.

O “leitor astuto” deve ter reparado que a tag <apply-content/> está sozinha na linha 18, sem nenhuma tag <apply template="*"> em volta.

E você sabe qual é o motivo dessa solidão? Melhor ainda, você sabe por quê o nome de todos os templates começam com underscore (_), exceto o index.tpl?

A primeira pergunta eu vou responder. A segunda eu quero que você descubra e me conte nos comentários ali embaixo (quem arrisca um chute?).

Toda essa solidão da tag <apply-content/> é porque o conteúdo que será substituído ali na linha 18 é justamente o conteúdo dos templates que aplicarem o _default.tpl (ex.: index.tpl, contato.tpl e sobre.tpl).

Para ficar mais claro, veja a estrutura de um template de exemplo dummy.tpl:

1
2
3
<apply template="_default">
    Conteúdo que será inserido no <apply-content/> da linha 18 do "default.tpl"!
</apply>

No caso da nossa index.tpl, o conteúdo que vai entre as tags <apply> é precisamente “o meio” da index.html que usamos de base, ou seja, a lista de categorias, o carrossel e o grid de presentes.

Renderizando uma nova index.html com o Heist

Na seção anterior nós incorporamos o “Heist Estripador” e agora é a hora de juntar as partes .tpl para trazer nossa index.html de volta à vida.

As funções do Heist para o processamento de templates vem em 2 sabores: Sabor Compilado com Flocos de Performance e Sabor Interpretado com Cauda de Mais Fácil de Usar.

Por razões didáticas, nós degustaremos a segunda opção neste tutorial.

Eu colocarei o código abaixo e, em seguida, eu explicarei linha a linha o que está acontecendo. Lembrando que se você não entender qualquer coisa é só dar um gritão nos comentários!

OBS.: Para os experts em Haskell, eu deliberadamente omiti a lista de identificadores importados e a assinatura das funções por dois motivos: (i) demonstrar a concisão/expressividade de Haskell; (ii) não criar resistência junto aos iniciantes em Haskell com “detalhes de tipos”. Eu não considero isso prejudicial porque o código continua correto e porque muitas pessoas vêm de linguagens interpretadas e já estão acostumadas com um estilo de codificação “sem tipos”.

Então continuando… Toma-lhe Haskell :point_down:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
{-# LANGUAGE OverloadedStrings #-}

import Heist
import Heist.Interpreted
import Blaze.ByteString.Builder
import Control.Lens
import qualified Data.Text.Lazy          as TL
import qualified Data.Text.Lazy.Encoding as TL
import qualified Data.Text.Lazy.IO       as TL
 
heistConfig = emptyHeistConfig
    & hcNamespace         .~ ""
    & hcLoadTimeSplices   .~ defaultInterpretedSplices
    & hcTemplateLocations .~ [loadTemplates "templates/"]

main = do
    heistState <- either (error . unlines) id <$> initHeist heistConfig
    Just (builder, _) <- renderTemplate heistState "index"
    TL.writeFile "site/index.html" . TL.decodeUtf8 . toLazyByteString $ builder

A linha 1 especifica uma extensão de linguagem que possibilita uma conversão implícita entre “tipos string”.

WHAAAAAT?!?!

Acontece que Haskell tem um sistema de tipos forte, e diferentemente do que ocorre em outras linguagens, como C ou Java, não existe conversão implícita. Por exemplo, se uma função esperar um Long como argumento e você passar um Int, o seu programa não vai compilar. Mesmo um Int “cabendo” em um Long, o compilador requer que você programador faça a conversão. Se você vem de outras linguagens, isso pode parecer uma limitação de Haskell, mas acredite, esse é na verdade um dos pontos fortes de Haskell.

Enfim, em Haskell existem vários “tipos string”. Os principais são:

  • String ou [Char]
  • Text
  • ByteString.

Por padrão, o compilador atribui a literais como "templates/" (linha 14), "index" (linha 18) e "site/index.html" (linha 19), o tipo String. Sendo assim, esses literais necessitam de uma conversão explícita para Text ou ByteString ao serem usados como argumentos de funções que esperam algum desses tipos, como a loadTemplates, a renderTemplate e a writeFile.

É para evitar o trabalho braçal de fazer essas conversões manualmente e não poluir o código que nós usamos a extensão OverloadedStrings, isto é, a OverloadedStrings faz (transparentemente) o trabalho sujo por nós.

As linhas 3–9 contêm os módulos que nós importamos. Uma boa prática é listar junto a cada módulo os identificadores que são efetivamente usados no seu código (tipos de dados, funções e operadores). Eu omiti essas informações apenas para o código não ficar muito extenso, mas eu recomendo o exercício de estudar os módulos das dependências listadas no .cabal para entender da onde veio cada coisa, principalmente se você for iniciante.

Nas linhas 11–12 nós definimos a função constante heistConfig, que é usada para inicializar o Heist chamando-se initHeist na linha 17.

A função main faz apenas 3 coisas:

  • Linha 17: Inicializa o Heist e, em caso de sucesso, recupera o HeistState correspondente. Em caso de erro, encerra a execução e escreve a exceção no console.
  • Linha 18: Renderiza o template index.tpl e, em caso de sucesso, recupera o Builder correspondente. Em caso de erro, encerra a execução e escreve a exceção no console.
  • Linha 19: Decodifica a sequência de bytes que compõe o site usando o padrão UTF-8 e escreve o resultado no caminho site/index.html.

Eu não entrarei em mais detalhes sobre a heistConfig e a main porque eu já expliquei direitinho cada uma dessas funções na vídeo-aula. Quem assistiu viu que eu faço, inclusive, um paralelo com Java, explicando como seria um código OO equivalente. Então, se você ainda não assistiu a aula, aproveita e corre lá.

Agora é só executar esse código para “ressuscitar” a index.html. Como eu expliquei anteriormente, você pode fazer isso de duas formas:

  • Executar stack build && stack exec gift-list no host
  • Executar stack ghci e chamar a função main diretamente de dentro do GHCi

Com um pouco de sorte, tudo ocorrerá bem, e você deve ver um index.html fresquinho na pasta site/. :joy:

Criando a “/sobre” e entendendo a <bind>

Agora que nós já entendemos mais ou menos como o Heist e a <apply> funcionam é hora de fazer um pequeno exercício de fixação…

Nossa missão é gerar uma nova página: sobre.html — onde nós colocaremos informações super importantes sobre o nosso site.

Reflita por um instante: por onde você começaria esta árdua tarefa?! :metal:

SPOILER ALERT: abaixo tem a cola, então realmente pare por um momento e tente sozinho gerar a sua sobre.html antes de continuar. É um bom exercício! :wink:

(…) Tentou resolver?!

Cuidado que quando você menos esperar a resposta vai aparecer… :point_down: :point_down: :point_down:

"Óh a resposta vino"

Bom, para atingir o checkpoint dessa missão, nós faremos exatamente a mesma coisa que fizemos com a dummy.tpl e a index.tpl. Trocando em miúdos, nós criaremos um sobre.tpl, aplicaremos o template default.tpl sobre ele e adicionaremos o conteúdo:

1
2
3
4
5
6
7
8
9
10
11
12
<apply template="_default">
    <bind tag="pageTitle">Sobre esta página</bind>

    <h1>Sobre esta página</h1>

    <p>Este é um tutorial sobre a geração de páginas HTML usando Haskell, ou
    melhor, o sistema de <em>templates</em> Heist.</p>

    <p>Para mais informações, acesse o
        <a href="https://mavins.com.br">site</a> da Mavins!
    </p>
</apply>

Epa, pera aí! Apareceu um treco esquisito… Quem que é essa tal de <bind> sobrando ali na linha 2?

Essa tal de <bind> é a novidade dessa seção e o que ela faz é, literalmente, o significado de bind traduzido do inglês para o português, que quer dizer associação.

Em outras palavras, a tag <bind> permite que nós associemos um valor a um identificador, rótulo ou tag personalizada. Por exemplo, no código acima, nós associamos à tag pageTitle o valor Sobre esta página.

O nome pageTitle é completamente arbitrário. Você poderia usar tituloDaPagina, brubles ou carlosAlberto, se quisesse.

Eu escolhi pageTitle porque o objetivo, nesse caso, é atribuir “dinamico-estaticamente” em cada página do site o conteúdo da tag <title>, definida lá no _head.tpl, para que cada página tenha um título diferente.

Para o exemplo ficar completo, nós devemos alterar também os arquivos index.tpl e _head.tpl. Lá no _head.tpl, a linha que define o <title> deve mudar para:

1
<title><pageTitle/></title>

Já no index.tpl, devemos acrescentar o <bind> correspondente:

1
<bind tag="pageTitle">Lista de Presentes</bind>

Pegou a ideia?! :wink:

A última coisa que você precisa saber por enquanto é uma peculiaridade sintática do Heist na hora de interpolar splices em atributos. Neste caso, nós usamos a notação ${...}, como no exemplo a seguir, onde nós definimos o menu de navegação:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!-- Código em "_nav.tpl" -->
<ul class='nav navbar-nav navbar-right'>
    <li>
        <a href='${nav1Href}'><nav1Text/></a>
    </li>
    <li>
        <a href='${nav2Href}'><nav2Text/></a>
    </li>
</ul>

<!-- Código em "_default.tpl" -->
<apply template="_nav">
    <bind tag="nav1Text">Início</bind>
    <bind tag="nav1Href">index.html</bind>

    <bind tag="nav2Text">Sobre</bind>
    <bind tag="nav2Href">sobre.html</bind>

    <apply-content/>
</apply>

Apenas para contextualizar, a filosofia por trás dessas tags <bind> e <apply> do Heist é “ajudar” o(a) designer e promover uma “separação quase perfeita” entre view e lógica de negócio.

A ideia de ajudar o designer está fundamentada no fato de que ele(a) não precisa aprender um novo dialeto na hora de criar as páginas. Com exceção dessa sintaxe especial usada nos atributos (mas que também não tem nada de complicada), no final das contas é tudo XML.

Além disso, como nós veremos na seção a seguir, é possível associar código Haskell a uma tag de forma similar ao que fazemos com a <bind>. Assim, o designer pode, por exemplo, descrever o grid de presentes usando tags como: <gifts/>, <giftName/> e <giftPrice/>.

Nesse caso, ficaria a cargo do programador especificar em código Haskell o “for” que iteraria sobre a lista de presentes. Essa característica do Heist resulta em uma melhor separação de preocupações (ou Separation of Concerns — SoC) em relação a outras “tecnologias de view”.

Compare, por exemplo, esse estilo de programação com o de tecnologias como o AngularJS, o Jinja (do Python), o PHP ou o próprio Jekyll. Em todos esses exemplos a view fica, naturalmente, poluída com código que contém “lógica”, ferindo um dos princípios básicos do modelo arquitetural MVC.

Programando splices em Haskell

Finalmente chegamos à última parte do nosso tutorial…

Nessa parte, ao invés de usar a tag <bind> para definir o valor dos splices diretamente nos templates, nós usaremos código Haskell.

É daí que vem a real dinamicidade em um sistema de templates.

Por exemplo, quando o Heist é integrado ao Snap Framework, nós podemos escrever código Haskell para exibir uma lista de presentes atualizada recuperando os registros correspondentes de um banco de dados, fornecer uma experiência de navegação personalizada usando informação armazenada em coookies ou, até mesmo, trocar completamente o layout da página dependendo das permissões do usuário.

Neste tutorial, nós construiremos um splice chamado loginLogoutSplice para exibir a string “Logout”, caso o usuário esteja “logado”, e “Login”, caso contrário.

Nós usaremos uma função isLoggedIn que retorna True ou False para simular, respectivamente, cada um dos 2 estados possíveis: “logado” e “deslogado”:

1
isLoggedIn = True

Se você estiver achando esse exemplo meio sem sentido, dado que estamos gerando páginas estáticas, não ache. Existe uma lógica por trás dele…

Acontece que no Snap Framework, que nós estudaremos eventualmente em algum post no futuro, existe um snaplet de autenticação que possui uma função similar a nossa isLoggedIn. A única diferença é que, no caso do Snap, essa informação é obtida da sessão do usuário (como deveria ser), ao invés de estar hardcoded no código-fonte.

Eu estou apenas preparando o caminho para um futuro glorioso e recheado de programação funcional… :wink:

Então, mãos à obra! :muscle:

Comecemos editando o item de “Login/Logout” no nosso menu de navegação em _nav.tpl:

1
2
3
<li>
    <a href='${loginLogoutHref}'><loginLogoutText/> <loginLogoutIcon/></a>
</li>

O código acima simplesmente interpola 3 tags: loginLogoutHref, loginLogoutText e loginLogoutIcon — cujo bind nós faremos usando código Haskell. Lembrando que, desde que você mantenha a consistência no código, você pode dar o nome que você quiser para essas tags.

Vamos primeiro adicionar ao arquivo Main.hs os splices associados às tags loginLogoutHref e loginLogoutText:

1
2
3
4
5
6
7
8
9
loginLogoutHrefSplice =
    if isLoggedIn
        then textSplice "#logout"
        else textSplice "#login"

loginLogoutTextSplice =
    if isLoggedIn
        then textSplice "Logout"
        else textSplice "Login"

Simples assim! :grin: :v:

Eu deixei o splice loginLogoutIcon para depois porque é claro que eu ia dar uma complicadinha, né?! :stuck_out_tongue_closed_eyes:

Ao invés de só tacar o HTML hardcoded com os ícones de “Login” e “Logout” do Bootstrap, eu resolvi usar os combinators da biblioteca blaze-html porque sim .

Com os devidos “imports”, o código fica assim:

1
2
3
4
5
6
7
8
9
loginLogoutIconSplice = return . renderHtmlNodes $
    span ! class_ iconClass
         ! customAttribute "aria-hidden" "true"
         $ ""
  where
    iconClass =
        if isLoggedIn
            then "glyphicon glyphicon-log-out"
            else "glyphicon glyphicon-log-in"

Da'fuck

Acalme-se! Até porque você não precisa se preocupar em entender esse código…

Eu só coloquei ele aí porque eu empolguei e queria te mostrar como Haskell é legal queria te mostrar uma forma bem elegante e type-safe de se escrever HTML. :grin:

Para simplificar, o que esse splice faz é botar o ícone:

  • → se isLoggedIn for True.
  • → se isLoggedIn for False.

Já definimos os splices, agora para finalizar só resta associá-los às tags loginLogoutHref, loginLogoutText e loginLogoutIcon.

O jeito mais fácil de fazer isso é alterando a heistConfig:

1
2
3
4
5
6
7
8
heistConfig = emptyHeistConfig
    & hcNamespace          .~ ""
    & hcLoadTimeSplices    .~ defaultInterpretedSplices
    & hcInterpretedSplices .~ mconcat [ "loginLogoutHref" ## loginLogoutHrefSplice
                                      , "loginLogoutText" ## loginLogoutTextSplice
                                      , "loginLogoutIcon" ## loginLogoutIconSplice
                                      ]
    & hcTemplateLocations  .~ [loadTemplates "templates/"]

Nós usamos o operador ## para fazer as associações, concatenamos os splices com mconcat e “atribuímos” o resultado à hcInterpretedSplices.

Prontinho! :grin: :v:

Agora tudo o que você tem que fazer é gerar o site novamente e checar se deu tudo certo. Experimente trocar a definição de isLoggedIn para isLoggedIn = False e veja se o resultado foi o esperado.

Conclusão

Neste artigo nós aprendemos sobre o sistema de templates Heist, que é o sistema de templates padrão do framework Web Snap Framework. Contudo, não existe uma dependência direta entre o Heist e o Snap, e tanto o Heist quanto o próprio Snap podem ser usados de forma independente um do outro.

O Heist, em si, é uma biblioteca Haskell que pode ser usada, dentre outras, para a geração de páginas estáticas. Neste tutorial, nós usamos o Heist para gerar uma página estática construída com o Bootstrap.

Esse foi apenas o primeiro tutorial de uma série que vem por aí.

Meu principal objetivo foi tentar despertar em você o interesse ou, pelo menos, curiosidade sobre programação funcional e, mais especificamente, sobre Haskell.

A ideia é incrementar o código que desenvolvemos aqui em outros tutoriais para que ele se aproxime do que seria uma aplicação Web real. Para isso, nós ainda precisamos aprender a usar o Snap Framework para servir a aplicação; conectar com um banco de dados; integrar um sistema de pagamentos e fazer o deploy em algum lugar na Internet.

Antes de finalizar o post, você lembra quando eu falei lá em cima para você ler o tutorial até o final de olhos bem abertos porque, eventualmente, eu iria precisar da sua ajuda?! :grin:

Pois é, animação++ porque essa hora chegou! :bowtie:

A verdade é que se você realmente leu esse tutorial até aqui, então é porque #tamojunto nessa de desenvolver o site do meu casamento em Haskell, né não? Ou eu tô errado?!

Então já que eu tô certo eu preciso que você me ajude a escolher qual banco de dados e qual serviço de hospedagem nós vamos usar para completar os próximos tutoriais!

As opções de banco de dados, em ordem alfabética (não de preferência), são:

  1. MySQL
  2. PostgreSQL
  3. SQLite

As opções de hospedagem são:

  1. DigitalOcean
  2. Heroku

Então façam suas escolhas e corram lá em baixo nos comentários (o quanto antes) para me contar o que você escolheu!

Só uma ressalva: não vale escolher aleatoriamente. Tem que justificar nessa bagaça! :sunglasses:

Para não abusar da sua paciência, só vou pedir mais uma coisa…

Se você gostou desse tutorial (ou não), deixe um comentário no espaço abaixo. O seu feedback é sempre bem-vindo e muito importante para nos guiar na hora de produzir as vídeo-aulas, os tutoriais e os nossos cursos (em breve).

Um forte abraço, e até o próximo post!