Programação Funcional em PHP como aplicar.
Bem… PHP não é uma linguagem funcional, mas algumas técnicas da programação funcional podem ser utilizadas para melhorar o nosso código: melhor legibilidade e tornar mais fácil a manutenção do mesmo são vantagens que adquirimos ao usar o paradigma funcional de programação.
Por vários anos o desenvolvimento em PHP foi de modo procedural, tudo em um único arquivo com funções espalhadas por vários lugares. Depois da versão 5 foi possível escrever as aplicações php orientada a objetos.
A ideia é utilizar o melhor de cada paradigma seja Orientação a objetos ou funcional, fazer uma fusão de ambos um exemplo disso são as linguagens Scale, Kotline e também TypeScript que pode ser chamado de“Object-Functional”.
Programação funcional e PHP funcional:
Um resumo de como é a programação funcional:
Evita mudança de estado.
Uma vez que um objeto é criado, nunca será alterado. Não existem variáveis como as que usamos no PHP.
Usa sistema de tipagem complexa.
Um tipo é um classificador para um valor ele nos dá a informação sobre o que será aquele valor no tempo de execução. Produzem funções, por exemplo, com mais verbosidade(como você pode ver o que a função espera e que resultado retornará), mas como recompensa para isto, é que elas podem detectar erros no momento de compilação na sua IDE enquanto estamos escrevendo.
Funções puras x impuras
Funções puras:
Não existem globais; valores não são passados por referências;
Não tem efeitos colaterais;
Não utiliza nenhum tipo de estrutura de repetição (for, while, foreach…)
Funções impuras:
- Alterações de variáveis globais;
- Podem modificar parâmetros de entrada;
- Podem lançar exceções;
- Podem fazer qualquer operação de I/O(como usar a conexão com o banco de dados, redes ou arquivos);
- Podem ter efeitos colaterais;
- Vamos ver alguns exemplos práticas de programação funcional em php.
Evitando variáveis temporárias
Sem variáveis temporárias nós evitamos ter um if local o qual pode gerar um resultado indesejado.
function isPositive(int $number) { if ($number > 0) { $status = true; } else { $status = false; } return $status; }
Nós podemos remover a variável temporária retornando diretamente o status:
function isPositive(int $number) {
if ($number > 0) {
return true;
}
return false;
}
A última refatoração remove o if e finalmente temos o padrão funcional.
function isPositive(int $number) {
return $number > 0 ;
}
Funções pequenas:
Elas devem seguir o padrão de responsabilidade única: fazer apenas uma única coisa.
Isso significa que elas são fáceis de testar.
Elas podem ser compostas.
function sum(int $number1, int $number2) { return $number1 + $number2; }echo sum(sum(3, 4), sum(5, 5));// 17
Nós poderíamos mudar e o resultado seria o mesmo:
`echo sum(7, sum(5, 5));
// 17
Isso é chamado de referência transparente; uma função que pode ser substituída pelo seu resultado e o resultado final não é alterado.
Remova os ifs
Isto pode ser um pouco difícil quando nós estamos acostumados a escrever de uma forma imperativa.Neste exemplo, calcula o produto de um valor em um array. Para fazer nós usamos alguns agregadores e um loop para itera-lo.
function productImperative(array $data) { if (empty($data)) { return 0; } $total = 1; $i = 0; while ($i < count($data)) { $total *= $data[$i]; $i++; } return $total;}
Em casos como este, que existe alguma ação repetitiva o if pode ser removido com a recursão.
function product(array $data) { if (empty($data)) { return 0; } if (count($data) == 1) { return $data[0]; } return array_pop($data) * product($data); }
Este exemplo é bem simples e seria melhor resolvê-lo com reduce:
`echo array_reduce([5, 3, 2], function($total, $item) {
return $total * $item;
}, 1);
// 30
Nossas funções php geralmente precisam de uma entrada de recursos externos para o seu produzir o seu retorno. Isso pode dificultar o uso de funções puras. Nesses casos, nós devemos separar o código puro de um não puro. Por exemplo: Usando o codigo de php.net para a leitura de um arquivo.the HTML source of a URL.
$lines = file(‘https://www.google.com/'); foreach ($lines as $line_num => $line) { echo htmlspecialchars($line) . “ \n”;}
Lê o conteúdo de um site externo (entrada externa) e mostra o resultado no console.(saída externa) Podemos fazer alguma mudança com a programação funcional neste caso? Com certeza, vamos dividir o código em duas funções diferentes.
function getFileContent($file) { return file($file);}function formatLines($lines) { return array_map(function($line) { return htmlspecialchars($line) . “\n”; }, $lines);}
print_r(formatLines(getFileContent(‘https://www.google.com/’)));
O Resultado é o mesmo, mas agora nós temos 2 funções:
Uma função impura: (getFileContent) que é facilmente simulada no nosso teste.
Uma função pura: (formatLines) que sempre retorna o mesmo resultado e que é simples fazer um teste unitário.
Não use loops para iterar arrays
Nós precisamos fazer uma iteração nos elementos de um array para alterar cada um dos seus conteúdos. Exemplo: Recebemos uma lista de usuários do banco de dados e precisamos retornar o modelo que a nossa aplicação espera.
De uma forma imperativa nós fazemos algo assim: usamos o foreach para dizer o que o computador deve fazer.
Em estilo mais funcional seria:
function findUsersMap() { return array_map(“convertUser”, getUsers()); }function convertUser(array $user) { return new UserDTO($user); }
Usamos o array_map ao invés do foreach e criamos uma função pura que o único propósito é converter o formato do usuário. Esta versão é muito mais legível que as anteriores.
Filter
Iterando em um array, mas retornando apenas os elementos que atendem a uma condição.
Na lista de usuário nós queremos exibir apenas aqueles que vivem em Barcelona. Novamente nós precisamos percorrer todo o array e checar a cidade de cada um.
function getUsersFromBcn(array $users) { $bcnUsers = []; foreach ($users as $user) { if ($user->getCity() == “Barcelona”) { $bcnUsers[] = $user; } } return $bcnUsers; }
Ou poderíamos pegar apenas os usuários de Barcelona
function getUsersFromBcn(array $users) { return array_filter($users, “isFromBcn”);}function isFromBcn(UserDTO $user) { return $user->getCity() == “Barcelona”; }
Este código é muito mais simples e fácil de ser testado sem variáveis temporárias nem foreach + if loops.
Reduce / fold
Reduz um array e retorna seu valor.
Agora nós queremos calcular a média dos filhotes na cidade de Barcelona. Iteramos os usuários de Barcelona e calculamos os números de filhotes.
function getAvgPets(array $users) { $numPets = 0; foreach ($users as $user) { $numPets += $user->getPets(); } return $numPets / count($users); }
Ou nós podemos somar o número de filhotes.
function getAvgPets(array $users) { return array_reduce($users, “getTotalPets”, 0) / count($users); }function getTotalPets($total, UserDTO $user) { return $total + $user->getPets(); }
Usando pipelines
Vamos agrupar todas as condições
echo getAvgPetsReduce(getUsersFromBcn(findUsers()));
Laravel collection
Se nós tivermos múltiplas condições nós podemos fazer uma grande linha que pode ser difícil a leitura. Infelizmente, para resolver este problema, não temos um recurso nativo em PHP. Felizmente, existem algumas boas bibliotecas para usar pipelines.
Laravel é um framework que tem um grande biblioteca para trabalhar com arrays, o qual são chamadas de collections e pode ser usada fora do laravel, instanciada via composer.
Usando esta biblioteca, os objetos são imutáveis e o código é mais legível e mais declarativo.
Usando nosso exemplo da média de filhotes em Barcelona seria:
$collection = collect(getUsers());echo $collection->map(“convertUser”)->filter(‘isFromBcn’)->map(“getListPets”)->average();
A ordem das ações é muito clara.
1 Converter os usuários;
2 Filtrar por somente aqueles que são de Barcelona;
3 Pegar a lista de filhotes por usuários;
4 Obter a média (método da biblioteca que faz o reduce + cálculo da média);
Conclusão PHP Funcional:
PHP, não é 100% funcional e mudamos a maneira de programar para o modo funcional e não é uma tarefa fácil. Podemos começar aplicando alguns desses simples princípios e tornando o nosso código mais simplificado, mais legível, testável e com poucos efeitos colaterais. Nós podemos pensar de forma mais declarativa e passo a passo nós seremos mais funcionais.
Fim do artigo: o texto original você pode conferir no endereço do autor:
Caso você encontre algum erro na tradução, por favor entre em contato 🙂