Reflexões Funcionais com Julia Language — Parte I

Dec 01 2022
Explorando a programação funcional com Julia Julia é considerada uma linguagem de propósito geral dinamicamente tipada que oferece suporte a vários paradigmas de programação. Estritamente falando, embora Julia não seja uma Functional Programming Language (FP), ela a suporta.

Explorando a programação funcional com Julia

Imagem do autor

Julia é considerada uma linguagem dinamicamente tipada de propósito geral que suporta múltiplos paradigmas de programação. Estritamente falando, embora Julia não seja uma Functional Programming Language (FP), ela a suporta.

Então, o que é uma linguagem de programação funcional “pura”? São linguagens projetadas com funções matemáticas que usam expressões condicionais e recursão para realizar cálculos. Eles usam dados imutáveis, avaliação preguiçosa e outras construções para “impor” o modo de programação FP. Essas linguagens podem ser bastante concisas e opinativas. Um bom exemplo dessa classe de linguagem é Haskell.

Julia, no entanto, fica mais do que feliz em dar o poder ao programador, em vez de colocar limitações e barreiras no caminho para forçar a adesão a este ou aquele paradigma.

Julia é uma boa linguagem para programar de forma Funcional? Eu acredito que sim. Então, por que Julia é boa em Programação Funcional? Por vários motivos, incluindo metaprogramação, objetos de primeira classe, sistema de tipo poderoso, despacho múltiplo e muitos outros motivos.

FP está ganhando popularidade ultimamente. Os estados de objetos mutáveis ​​são o novo código espaguete e a simultaneidade apenas aumenta a complexidade. FP suporta programação paralela e simultaneidade, levando a menos bugs e código limpo. FP tem em suas raízes conceitos matemáticos sólidos e ideias de um campo matemático emergente conhecido como Teoria das Categorias.

Vamos mergulhar de cabeça e começar a construir alguns conceitos.

Programação funcional em ação

Digamos que estou interessado em descobrir o cubo de alguns números, poderia escrever:

3 * 3 * 3

2 * 2 * 2

cube(x) = *(x,x,x)

cube(x) = x * x * x

Agora estou interessado em calcular algumas séries. Começaremos projetando uma função para calcular a soma dos inteiros de a até b:

Agora, não seria exagero supor que, em algum momento, eu estaria interessado em calcular também a soma dos cubos de números inteiros em um determinado intervalo. Nesse caso, minha função poderia ser a seguinte:

Não é difícil identificar o padrão de código presente nas duas definições de função acima. Outra dica é o tipo de soma implícita no nome da própria função. O que precisamos é de outro nível de abstração, para que possamos calcular a soma de qualquer série. Precisamos tornar o tipo de soma explícito e passá-lo como argumento para a função em vez de codificar no corpo da função.

Com a definição de função acima, tornamos o tipo de função a ser somado explícito no argumento. Anotei o parâmetro “f” para deixar claro que é uma função e não outro valor.

Agora, se eu quiser a soma dos cubos de 1 a 10, posso chamar assim:

Ou se eu quiser a soma dos inteiros de 1 a 10, posso escrever:

Explorando funções de Julia

Em Julia, as funções são objetos de primeira classe e podem ser passadas como argumentos para outras funções. Isso abre a porta para soluções ainda mais elegantes com níveis mais profundos de abstração.

Podemos continuar construindo abstrações sobre abstrações. Também podemos canalizar funções juntas em cadeias. Julia é considerada uma linguagem altamente componível. Aqui é quando um pouco de conhecimento da Teoria das Categorias pode ajudá-lo a se tornar um programador melhor. A Programação Funcional tem raízes profundas em fundamentos matemáticos.

O exemplo acima mostra maneiras equivalentes de combinar e compor funções juntas.

Os exemplos acima mostram como passar funções como argumentos pode nos ajudar a alcançar níveis mais profundos de abstração em nosso código. Mas não é preciso parar por aí. Podemos criar funções cujos valores de retorno sejam elas próprias funções. Tome o código a seguir como exemplo. Considere uma função f que retorna o valor médio entre x e f(x) . Chamaremos essa função de average_damp .

julia> average_damp(square)
#7 (generic function with 1 method)

Para obter nosso valor úmido médio de uma função quadrada em x=10 , chamamos com:

julia> average_damp(square)(10)
55.0

Imagem por autor

julia> dx = 0.00001;

julia> deriv(cube)(5)
75.00014999664018

O seguinte é um exemplo de tal função:

julia> withdraw(20)
80

julia> 

julia> withdraw(20)
60

julia> withdraw(20)
40

julia> withdraw(20)
20

julia> withdraw(20)
0.0

julia>

De qualquer forma, mais para vir na próxima vez…

Referências: “Estrutura e Interpretação de Programas de Computador”, Abelson e Sussman