2006/04/17

11 dicas para programar em O'Caml

Depois de algum tempo programando em Objective Caml, adquirí uma certa experiência na linguagem para fazer programas facilmente reusáveis e razoavelmente bem abstraídos, utilizando os recursos mais poderosos desta. Listei alguns dos itens que considero mais importantes para se observar ao programar em O'Caml, que podem fazer grande diferença na qualidade final do código gerado e da sua expressividade. Seguem estes, abaixo:
  • [1] Não utilize orientação a objetos, a menos que tenha uns bons anos de experiência com a linguagem, e que saiba o que está fazendo. A maior parte dos programadores tende a abusar deste recurso, erroneamente;

  • [2] Não programe imperativamente, a menos que tenha feito pelo menos uma dezena de programas puramente funcionais, e que saiba o que está fazendo. Neste caso, também há muitos abusos, e é importante notar que eles atrapalham a abstração do programa removendo as vantagens da programação funcional;

  • [3] Use o sistema de tipos para abstrair o dado que está tentando modelar. Ele é, até prova em contrário, poderoso o bastante para isso. Caso não for, você sempre pode isolá-lo como um tipo abstrato dentro de um módulo, e controlar a criação/manipulação do dado para forçar determinadas restrições extras que necessita;

  • [4] Use o sistema de módulos para isolar os tipos do resto do programa, delimitando a interface entre a sua(s) biblioteca(s) e o programa principal;

  • [5] Tipos, exceções, funções e métodos usados internamente pelo seu código devem permanecer internamente, e não serem declarados nas assinaturas (sig ... end, arquivos mli). Utilize a diretiva private, se necessário, para isolar construtores que são usados internamente;

  • [6] Evite as variantes polimórficas. Aliás, procure não usá-las nunca. Construtores de tipos encapsulados dentro de módulos evitam conflitos de nomes, e são tão poderosos quanto as variantes; além de permitirem uma checagem de tipos mais apurada;

  • [7] Casos excepcionais, especialmente os erros, devem permanecer casos excepcionais, e não devem ser inseridos nas abstrações, a menos que você realmente tenha um bom motivo. Um exemplo de como não se deve implementar uma abstração é o caso da biblioteca Netclient: no módulo Http_client, o tipo protocol pode assumir valor `Other, além do tipo da conexão (HTTP ou HTTPS) e das versões disponíveis neste, o que complica o código de tratamento deste tipo;

  • [8] Diminua o número de funções do seu módulo/classe usando parâmetros nomeados opcionais. Por exemplo, se você tiver:

    let add_with_callback callback msg = ...
    let add msg = ...

    Reduza a uma função só, da seguinte maneira:

    let add ~callback msg =
    match callback
    with
    None -> ...
    | Some c -> ...
    ;;

  • [9] Utilize funtores onde achar necessário. Por exemplo: um módulo principal que implementa certa funcionalidade, mas que pode ter vários comportamentos diferentes associados a ele, pode ser abstraído em um funtor. Faz-se isto isolando-se cada aspecto comportamental como um tipo de módulo e parametrizando o módulo principal a partir desses das assinaturas dos módulos de comportamento. Desta forma, o módulo principal parametrizado pelos módulos comportamentais resultará em um módulo final, implementando a funcionalidade o com o comportamento desejado. O resultado é uma abstração do código em comum e uma boa extensibilidade no programa;

  • [10] Evite o uso de if-then-else: utilize guards quando fizer um pattern-matching. De preferência, use um pattern-matching no lugar de um if-then-else quando possível: isto facilita a legibilidade e flexibilidade do código;

  • [11] Aprenda a usar o ocamldoc e gere documentação para seu código, mesmo que sem comentários adicionais.
Obviamente seguir só estes passos não vai resultar em um código melhor. Mas, com certeza, eles são muito úteis para se fazer programas e bibliotecas com interfaces mais sãs e implementações mais alto-nível.

E, por enquanto, é isso.