A melhor maneira de aprender alguma coisa é escrever sobre ela. Então aqui estou eu, tentando aprender programação funcional.
Minha ferramenta de escolha foi o Racket, um dialeto de Scheme que está crescendo a ponto de ser chamado de linguagem.
Eu estou tentando ajustar a linguagem para compor funções da maneira que o Factor faz. Então, aqui estão os meus dois primeiros experimentos.
O que eu estou tentando fazer?
No estilo funcional, é comum fazer “pipe” de dados por meio de mais de uma função. Vamos dizer que eu quero pegar a primeira coluna de um arquivo CSV. Eu poderia fazer algo como isto aqui:
(get-first (split-columns (open “some.csv”)))
Para entender isso, temos que ler de trás para a frente ou manter na memória todas as operações até chegarmos a “… CSV”. Se você pegar um pouco mais de operações, as coisas ficam realmente difíceis de entender.
Eu quero escrever a mesma coisa da seguinte maneira:
(-> (open “some.csv”) split-columns get-first)
Agora eu posso ler da esquerda para a direita: abrir o arquivo, dividi-lo e obter a primeira coluna. Essa é a composição de função como é feita por linguagens de programação concatenativas (como a Factor, mencionada acima).
Composição simples usando macros
Vamos tentar algo simples. Adicione um a um número e converta-o para string. É fácil e rápido com macros, quando você descobre como fazer.
#lang racket
(define-syntax ->
(syntax-rules ()
[(-> a) a]
[(-> b ... a) (a (-> b ...))]))
(-> 1 add1 number->string)
; Exactly the same as:
(number->string (add1 1))
Essa é uma macro recursiva. Quando Racket encontra “->” como a primeira coisa após os parênteses, ele tenta corresponder contra dois padrões:
; if there is just one argument after "->", return it [(-> a) a] ; if there is more arguments, apply (execute) the last one ; and call "->" again with remaining arguments as its parameter [(-> b ... a) (a (-> b ...))]
Macros em Racket são surpreendentemente simples quando você as entende, além de não haver nenhuma penalidade quanto ao desempenho enquanto elas são expandidos na fase de compilação. No entanto, há uma limitação: ela não é uma função. Isso significa que eu não posso passá-la como um argumento para funções.
Então, vamos tentar novamente, mas, antes disso, o que são macros?
Interlúdio: o que são macros?
Macros se parecem com funções quando você as usa, mas na verdade elas modificam o código antes da execução. Pense que você pode modificar o seu AST antes de o programa ser executado.
Isso é incrivelmente poderoso, e o principal uso é estender a linguagem de maneiras que as funções não podem.
Composições simples usando funções
Funciona exatamente da mesma forma que a versão macro, mas é um pouco diferente na parte interna.
#lang racket (define/match (-> . ps) [((list a)) a] [((list b ... a)) (a (apply -> b))]) (-> 1 add1 number->string) ; Exactly the same as: (number->string (add1 1))
Novamente, é uma função recursiva. O “.ps” significa “qualquer número de argumentos”.
; if I have just one argument, return it [((list a)) a] ; if I have more arguments, call the last one (as a function) ; and then call "->" again with the remaining arguments [((list b ... a)) (a (apply -> b))]
Conclusão
Elas são simples e ainda não é o que o Factor faz, mas é um pequeno passo!
A segunda versão lógica parece a mesma para qualquer linguagem com funções como cidadãos de primeira classe (eu me pergunto como seria em JavaScript).
Particularmente em Racket, há “compose1“, que é semelhante, mas as funções são aplicadas em ordem inversa. Além disso, na minha versão, todas as funções se limitam a apenas um argumento.
***
Ronie Uliana faz parte do time de colunistas internacionais do iMasters. A tradução do artigo é feita pela redação iMasters, com autorização do autor, e você pode acompanhar o artigo em inglês no link: https://medium.com/@ronie/function-composition-part-1-3de098d8da50#.y1pb5dck5



