Você pode evitar a programação funcional como uma política?

Eric Elliott Blocked Unblock Seguir Seguindo 21 de março de 2018 Política por Nick Youngson CC BY-SA 3.0 Alfa Stock Images

A programação funcional penetrou na maior parte do mundo da programação mainstream – grande parte do ecossistema JS, Linq for C #, e funções de ordem ainda maior em Java. Este é o Java em 2018:

 getUserName ( usuários, usuário -> user.getUserName ()) ; 

FP é tão útil e conveniente, que encontrou o seu caminho em todas as modernas linguagens de programação em uso mainstream que eu posso pensar.

Mas não são todos os arco-íris e filhotes. Muitos desenvolvedores estão lutando com essa mudança tectônica em como pensamos sobre software. Francamente, é difícil encontrar um trabalho JavaScript hoje que não requeira algum conforto com conceitos de programação funcional.

O FP está no coração de ambos os frameworks dominantes, React (evitando DOM mutável compartilhado é a motivação para sua arquitetura e fluxo de dados unidirecional) e Angular (RxJS é uma biblioteca de operadores utilitários que atuam em streams através de funções de ordem – é usado extensivamente em todo o Angular). Redux e ngrx / store também: funcional em ambos os casos.

A programação funcional pode ser um tópico intimidador para os desenvolvedores novatos, e no interesse de uma integração rápida, alguém em sua equipe pode sugerir que você facilite a adaptação da sua equipe à base de código, evitando o FP como uma política.

Para gerentes não familiarizados com o que é FP, ou sua difusão no ecossistema moderno de códigos, isso pode soar como uma sugestão sensata. Afinal, a OOP não serviu bem ao mundo do software por 30 anos? Por que não continuar fazendo isso?

Vamos entreter essa noção por um momento.

O que significaria mesmo “banir” o PF como política?

O que é programação funcional?

Minha definição favorita de programação funcional é esta:

A programação funcional é um paradigma de programação que utiliza funções puras como a unidade atômica de composição, evitando estados mutáveis compartilhados e efeitos colaterais.

Uma função pura é uma função que:

  • Dada a mesma entrada, sempre retorne a mesma saída
  • Não tem efeitos colaterais

A essência do FP realmente se resume a:

  • Programa com funções
  • Evite estados mutáveis compartilhados e efeitos colaterais

Quando você os coloca juntos, você obtém desenvolvimento de software usando funções puras como blocos de construção (a “unidade atômica de composição”).

Incidentalmente, de acordo com Alan Kay, o instigador de todas as OOP modernas, a essência da OOP é:

  • Encapsulamento
  • Passagem de mensagem

Portanto, a OOP é apenas outra abordagem para evitar o estado mutável compartilhado e os efeitos colaterais.

Claramente, o oposto de FP não é OOP. O oposto do FP é a programação procedural não estruturada.

O Smalltalk (onde Alan Kay estabeleceu as bases do OOP) é orientado a objetos e funcional, e a ideia de escolher um ou outro é completamente estranha e impensável.

O mesmo vale para o JavaScript. Quando Brendan Eich foi contratado para construir JavaScript, as ideias eram:

  • Esquema no navegador (FP)
  • Parecido com Java (OOP)

Em JavaScript, você pode tentar favorecer um ou outro, mas para melhor ou para pior, eles estão inextricavelmente ligados. O encapsulamento em JavaScript depende de encerramentos – um conceito da programação funcional.

As chances são muito boas que você já está fazendo alguma programação funcional, quer você saiba disso ou não.

Como NÃO fazer FP:

Para evitar a programação funcional, você teria que evitar o uso de funções puras. O problema é que agora você não pode escrever isso porque pode ser puro:

 const getName = obj => obj.name; 
const name = getName ({uid: '123', nome: 'Banksy'}); // Banksy

Vamos refatorar isso, então não é mais FP. Poderíamos fazer disso uma aula com uma propriedade pública. Como não está usando o encapsulamento, é difícil chamar isso de OOP. Talvez devêssemos chamar isso de "programação de objeto procedural":

 class User { 
construtor ({name}) {
this.name = nome;
}
getName () {
return this.name;
}
}
 const myUser = novo usuário ({uid: '123', nome: 'Banksy'}); 
nome da const = myUser.getName (); // Banksy

Parabéns! Acabamos de transformar duas linhas de código em 11 e introduzimos a probabilidade de uma mutação externa descontrolada. O que nós ganhamos?

Bem, nada, na verdade. De fato, perdemos muita flexibilidade e o potencial de economias significativas de código .

A função getName() anterior funcionava com qualquer objeto de entrada. Este também é (porque é JavaScript e podemos delegar métodos a objetos aleatórios), mas é muito mais complicado. Poderíamos deixar as duas classes herdarem de uma classe base comum – mas isso implica em relações que podem não precisar existir entre classes de objetos.

Esqueça a reutilização. Acabamos de esvaziá-lo pelo ralo. Aqui está o que a duplicação de código se parece:

 amigo da turma { 
construtor ({name}) {
this.name = nome;
}
getName () {
return this.name;
}
}

Um aluno útil entra em cena do fundo da sala:

“Crie uma aula pessoal, claro!”

Mas então:

 class Country { 
construtor ({name}) {
this.name = nome;
}
getName () {
return this.name;
}
}

“Mas obviamente esses são tipos diferentes. Claro que você não pode usar um método pessoal em um país! ”

Ao que eu respondo: "Por que não?"

Um dos benefícios surpreendentes da programação funcional é a programação genérica trivialmente fácil. Você pode chamá-lo de "genérico por padrão": A capacidade de escrever uma única função que funciona com qualquer tipo que satisfaça seus requisitos genéricos.

Nota: Para o povo Java, isso não é sobre tipos estáticos. Algumas linguagens FP possuem grandes sistemas de tipo estático e ainda compartilham esse benefício usando recursos como tipos estruturais e / ou de maior qualidade.

Este exemplo é trivial, mas a economia no volume de código que obtemos desse truque é monumental .

Ele permite que bibliotecas como o autodux gerem automaticamente lógica de domínio para qualquer objeto composto de pares getter / setter (e muito mais, além disso). Só esse truque pode reduzir o código da lógica do seu domínio pela metade ou melhor.

Não há mais funções de ordem superior

Como a maioria (mas não todas) as funções de ordem superior aproveitam as funções puras para retornar os mesmos valores com base nas mesmas entradas, você também não pode usar funções como .map() , .filter() , reduce() sem jogando em um efeito colateral, só para dizer que você não é puro:

 const arr = [1,2,3]; 
 const double = n => n * 2; 
const doubledArr = arr.map (double);

Torna-se:

 const arr = [1,2,3]; 
 const double = (n, i) => { 
console.log ('efeito colateral aleatório sem motivo.');
console.log (`Ah, eu sei, nós podemos salvar diretamente a saída
para o banco de dados e fortemente juntar nossa lógica de domínio para o nosso I / O. Vai ficar bem. Ninguém mais precisará multiplicar por 2, certo? `);
saveToDB (i, n);
return n * 2;
};
 const doubledArr = arr.map (double); 

RIP, composição de funções 1958–2018

Esqueça a composição sem pontos dos Componentes de Pedidos Superiores para encapsular suas preocupações transversais entre as páginas. Esta sintaxe declarativa conveniente está agora fora dos limites:

 const wrapEveryPage = compor ( 
com o Redux,
withEnv,
withLoader,
withTheme,
withLayout,
withFeatures ({initialFeatures})
);

Você terá que importar manualmente cada um deles para cada componente, ou pior – descer em uma hierarquia de heranças de classe inflexível e emaranhada ( corretamente considerada um anti-padrão pela maior parte do mundo sane, mesmo [especialmente?] Pelo design OO canon ).

Adeus, promessas e async / aguardam

Promessas são mônadas. Tecnicamente da teoria de categorias, mas eu também soube que eles são uma coisa de FP, porque Haskell os usa e usa mônadas para tornar tudo puro e preguiçoso.

Honestamente, perder tutoriais de monad e functor provavelmente é uma coisa boa. Essas coisas são muito mais fáceis do que as fazemos soar! Há uma razão pela qual eu ensino as pessoas a usar o Array.prototype.map e Array.prototype.map antes de apresentar os conceitos gerais de functores e mônadas.

Você sabe como usá-los? Você é mais do que meio caminho para entender functores e mônadas.

Então, para evitar FP

  • Não use nenhum dos frameworks ou bibliotecas mais populares do JavaScript (todos eles o trarão para o FP!).
  • Não escreva funções puras.
  • Não use muitos recursos internos do JavaScript: a maioria das funções matemáticas (porque elas são puras), métodos de cadeia e matriz não mutáveis, .map (), .filter (), .forEach (), promessas, ou assíncrono / aguardar.
  • Escreva classes desnecessárias.
  • Duplique (ou mais) sua lógica de domínio manualmente escrevendo getters e setters para literalmente tudo.
  • Adote a abordagem imperativa “legível e explícita” e confunda sua lógica de domínio com renderização e preocupações de E / S de rede.

E diga adeus a:

  • Depuração de viagem no tempo
  • Fácil desenvolvimento de recursos de desfazer / refazer
  • Rock solid, testes unitários consistentes
  • Testes gratuitos com Mock e D / I
  • Testes rápidos de unidade sem dependências na rede I / O
  • Bases de código pequenas, testáveis, depuráveis e passíveis de manutenção

Evite a programação funcional como política? Sem problemas.

Oh espere.

Texto original em inglês.