Pequenas quantidades de programação funcional são compreensíveis por pessoas que não são FP? [fechadas]

43

Caso : estou trabalhando em uma empresa, escrevendo um aplicativo em Python que lida com muitos dados em matrizes. Eu sou o único desenvolvedor deste programa no momento, mas provavelmente será usado / modificado / estendido no futuro (1-3 anos) por algum outro programador, neste momento desconhecido para mim. Provavelmente não estarei lá diretamente para ajudar, mas talvez dê algum suporte por e-mail se tiver tempo para isso.

Então, como desenvolvedor que aprendeu programação funcional (Haskell), eu tendem a resolver, por exemplo, filtrar assim:

filtered = filter(lambda item: included(item.time, dur), measures)

O restante do código é OO, são apenas alguns casos pequenos em que eu quero resolvê-lo assim, porque é muito mais simples e mais bonito para mim.

Pergunta : Hoje está bom escrever código como este?

  • Como um desenvolvedor que não escreveu / aprendeu FP reage a códigos como este?
  • É legível?
  • Modificável?
  • Devo escrever uma documentação como explicar a uma criança o que a linha faz?

     # Filter out the items from measures for which included(item.time, dur) != True

Perguntei ao meu chefe e ele apenas disse "FP é magia negra, mas se funcionar e for a solução mais eficiente, não há problema em usá-lo".

Qual a sua opinião sobre isso? Como programador não-FP, como você reage ao código? O código é "googable" para que você possa entender o que ele faz? Eu adoraria comentários sobre isso.

kd35a
fonte
14
Na minha opinião, muitas linguagens modernas permitem algum grau de programação funcional e uso que se torna cada vez mais comum. Pessoalmente, eu diria que vá em frente (pelo menos para as tarefas relativamente simples de filtragem / mapeamento).
Joachim Sauer
Não posso lhe dar uma resposta não afetada, porque gosto de FP. Seria uma boa ideia adicionar um comentário, se for além do simples.
Bruno Schäpper
3
Seu comentário pode ser mais claro; O filtro inclui elementos para os quais o teste é True, para que seu comentário possa ser lido: # Select the item's from measures for which included(item.time, dur) == Trueevitar um duplo negativo sempre melhora a compreensão.
Martijn Pieters
6
Você já pensou em usar a compreensão da lista? Eles são freqüentemente considerados mais "pitônicos" que mapa / filtro.
Phant0m
2
@MatjazMuhic Bem, isso é lindo ...;)
kd35a

Respostas:

50

É legível?

Para mim: Sim, mas eu entendi que a comunidade Python geralmente considera as compreensões de lista uma solução mais limpa do que usar map()/ filter().

De fato, o GvR até considerou abandonar completamente essas funções.

Considere isto:

filtered = [item for item in measures if included(item.time, dur)]

Além disso, isso tem o benefício de que a compreensão da lista sempre retornará uma lista. map()e, filter()por outro lado, retornará um iterador no Python 3.

Nota: Se você deseja ter um iterador, é tão simples quanto substituir []por ():

filtered = (item for item in measures if included(item.time, dur))

Para ser sincero, vejo pouca ou nenhuma razão para usar map()ou filter()no Python.

É modificável?

Sim, certamente, no entanto, há uma coisa para facilitar: tornar uma função, não uma lambda.

def is_included(item):
    return included(item.time, dur)
filtered = filter(is_included, measures)
filtered = [item for item in measures if is_included(item)]

Se sua condição se tornar mais complexa, isso será muito mais fácil, além disso, permitirá que você reutilize seu cheque. (Observe que você pode criar funções dentro de outras funções, isso pode mantê-lo mais próximo do local onde é usado.)

Como um desenvolvedor que não escreveu / aprendeu FP reage a códigos como este?

Ele procura na documentação do Python e sabe como funciona cinco minutos depois. Caso contrário, ele não deveria estar programando em Python.

map()e filter()são extremamente simples. Não é como se você estivesse pedindo para eles entenderem mônadas. É por isso que acho que você não precisa escrever esses comentários. Use bons nomes de variáveis ​​e funções, então o código é quase auto-explicativo. Você não pode prever que idioma os recursos que um desenvolvedor não conhece. Pelo que você sabe, o próximo desenvolvedor pode não saber o que é um dicionário.

O que não entendemos geralmente não é legível para nós. Assim, você poderia argumentar que não é menos legível do que a compreensão de uma lista, se você nunca viu nenhum deles antes. Mas, como Joshua mencionou em seu comentário, eu também acredito que é importante ser consistente com o que outros desenvolvedores usam - pelo menos se a alternativa não oferecer uma vantagem substancial.

phant0m
fonte
5
Pode valer a pena mencionar também gerador de expressões , que trabalham o mesmo que compreensões lista, mas geradores de retorno em vez de listas, como mape filterfazer em python 3.
James
@ James: Ótima idéia, eu os adicionei ao meu post. Obrigado!
Phant0m
2
Normalmente, trago reducecomo exemplo um contra-exemplo: você sempre pode usar um argumento de compreensão de lista , pois é relativamente difícil substituí-lo reducepor um gerador embutido ou compreensão de lista.
Kojiro
4
+1 por observar o idioma preferido do idioma em questão. A consistência do IMHO tem um valor tremendo e o uso de um idioma da maneira que uma parte significativa dos "falantes" pode oferecer mais facilidade de manutenção do que os comentários às vezes.
27512 Joshua Drake
4
+1 em "Ele pesquisou a documentação do Python e sabe como ela funciona cinco minutos depois. Caso contrário, ele não deveria estar programando no Python."
Bjarke Freund-Hansen
25

Como a comunidade de desenvolvedores está recuperando o interesse pela programação funcional, não é incomum ver alguma programação funcional em linguagens que eram originalmente totalmente orientadas a objetos. Um bom exemplo é o C #, onde tipos anônimos e expressões lambda permitem ser muito mais curtos e expressivos através da programação funcional.

Dito isto, a programação funcional é estranha para iniciantes. Por exemplo, quando, durante um curso de treinamento, expliquei aos iniciantes como eles podem aprimorar seu código por meio da programação funcional em C #, alguns deles não estavam convencidos e outros disseram que o código original era mais fácil de entender para eles. O exemplo que dei a eles foi o seguinte:

Código antes da refatoração:

var categorizedProducts = new Dictionary<string, List<Product>>();

// Get only enabled products, filtering the disabled ones, and group them by categories.
foreach (var product in this.Data.Products)
{
    if (product.IsEnabled)
    {
        if (!categorizedProducts.ContainsKey(product.Category))
        {
            // The category is missing. Create one.
            categorizedProducts.Add(product.Category, new List<Product>());
        }

        categorizedProducts[product.Category].Add(product);
    }
}

// Walk through the categories.
foreach (var productsInCategory in categorizedProducts)
{
    var minimumPrice = double.MaxValue;
    var maximumPrice = double.MinValue;

    // Walk through the products in a category to search for the maximum and minimum prices.
    foreach (var product in productsInCategory.Value)
    {
        if (product.Price < minimumPrice)
        {
            minimumPrice = product.Price;
        }

        if (product.Price > maximumPrice)
        {
            maximumPrice = product.Price;
        }
    }

    yield return new PricesPerCategory(category: productsInCategory.Key, minimum: minimumPrice, maximum: maximumPrice);
}

Mesmo código após refatoração usando FP:

return this.Data.Products
    .Where(product => product.IsEnabled)
    .GroupBy(product => product.Category)
    .Select(productsInCategory => new PricesPerCategory(
              category: productsInCategory.Key, 
              minimum:  productsInCategory.Value.Min(product => product.Price), 
              maximum:  productsInCategory.Value.Max(product => product.Price))
    );

Isso me faz pensar que:

  • não se preocupe se souber que o próximo desenvolvedor que manterá seu código terá experiência geral suficiente e algum conhecimento de programação funcional, mas:

  • caso contrário, evite a programação funcional ou coloque um comentário detalhado explicando ao mesmo tempo a sintaxe, as vantagens e as possíveis advertências de sua abordagem em comparação com uma não-funcional de programação.

Arseni Mourzenko
fonte
8
+1 se FP não tornar seu código mais legível para ninguém , você estará fazendo errado.
György Andrasek
7
Eu posso ver como, isoladamente, alguém que nunca viu FP antes poderia considerar o trecho anterior mais legível em relação ao último. Mas e se multiplicarmos isso por 100? Lendo as 100 frases curtas, quase sucintas, do tipo inglês, descrevendo o que vs milhares de linhas de encanamento como código. O grande volume de código, por mais familiar que seja, deve ser tão avassalador que a familiaridade não é mais uma vitória tão grande. Em algum momento, deve ser mais fácil para qualquer um se familiarizar com os elementos do FP e depois ler algumas linhas versus ler milhares de linhas de código familiar.
Esailija
7
O que mais me incomoda é que estamos satisfeitos em acreditar que é aceitável que os desenvolvedores não aprendam o estilo funcional. As vantagens foram comprovadas várias vezes, o paradigma é suportado pelas linguagens convencionais. Se as pessoas não têm a capacidade de entender técnicas funcionais, elas devem ser ensinadas ou devem ensinar a si mesmas, mesmo que não pretendam usá-la. Eu odeio o XSLT com uma paixão ardente, mas isso não me impediu de aprender, como profissional.
Sprague 15/05
2
@MainMa Seu argumento é completamente válido, eu deveria ter sido mais específico. O que quero dizer é que um desenvolvedor em qualquer idioma deve usar os recursos desses idiomas de maneira eficaz. Por exemplo, o uso de 'estilo funcional' em C # (usando a programação de filtro / mapa / redução de estilo ou funções de passagem / retorno) não é novidade e, portanto, acho que qualquer desenvolvedor de C # que não seja capaz de ler essas instruções deve atualizar seus habilidades ... provavelmente muito rapidamente. Eu certamente acho que todo desenvolvedor deve aprender um pouco de Haskell, mas apenas por razões acadêmicas. Isso é uma coisa diferente.
Sprague 16/05
2
@ Praga: Eu entendo o seu ponto e concordo com isso. É difícil esperar que, por exemplo, programadores C # comecem a usar paradigmas do FP, quando muitos desses programadores nem usam genéricos. Até que haja tantos programadores não qualificados, o nível de nossa profissão será baixo, especialmente porque a maioria das pessoas sem formação técnica não entende nada sobre o que é o desenvolvimento.
Arseni Mourzenko
20

Não sou programador de FP e recentemente modifiquei o código do meu colega em JavaScript. Houve uma solicitação de HTTP com um retorno de chamada que se parecia muito com a declaração incluída por você. Devo dizer que demorei algum tempo (como meia hora) para descobrir tudo (o código em geral não era muito grande).

Não houve comentários e acho que não houve necessidade de nenhum. Eu nem pedi ao meu colega para me ajudar a entender o código dele.

Considerando que estou trabalhando há cerca de 1,5 anos, acho que a maioria dos programadores será capaz de entender e modificar esse código, desde que eu fiz.

Além disso, como Joachim Sauer disse em seu comentário, muitas vezes existem partes do FP em vários idiomas, como o C # (indexOf, por exemplo). Muitos programadores que não são de FP lidam com isso com bastante frequência, e o trecho de código que você incluiu não é algo terrível ou incompreensível.

superM
fonte
1
Obrigado por seu comentário! Isso me deu mais informações sobre a outra perspectiva. É fácil tornar-se casa-cego, e então você não sabe como parecia que quando você não sabia FP :)
kd35a
1
@ kd35a, de nada. Felizmente, eu posso ser objetivo aqui))
superM
1
+1 por apontar que muitos idiomas agora contêm elementos do FP.
30812 Joshua Drake
LINQ é o exemplo principal de FP em C #
Konrad Morawski
3

Eu diria que sim definitivamente!

Existem muitos aspectos da programação funcional, e o uso de funções de ordem superior, como no seu exemplo, é apenas um deles.

Por exemplo, considero que escrever funções puras é extremamente importante para qualquer software escrito em qualquer idioma (onde "puro" não quero dizer efeitos colaterais), porque:

  • eles são mais fáceis de testar na unidade
  • eles são muito mais fáceis de compor do que as funções de efeito colateral
  • eles são mais fáceis de depurar

Também evito muitas vezes alterar valores e variáveis ​​- outro conceito emprestado do FP.

Ambas as técnicas funcionam bem em Python e em outras linguagens que normalmente não são classificadas como funcionais. Eles geralmente são suportados pela própria linguagem (ou seja, finalvariáveis ​​em Java). Assim, futuros programadores de manutenção não enfrentarão uma barreira enorme para entender o código.


fonte
2

Tivemos a mesma discussão sobre uma empresa em que trabalhei no ano passado.

A discussão dizia respeito a "código mágico" e se deveria ser encorajada ou não. Ao analisar um pouco mais, parecia que as pessoas tinham opiniões muito diferentes sobre o que realmente era "código mágico". Os que iniciaram a discussão pareciam significar principalmente que expressões (em PHP) que usavam estilo funcional eram "códigos mágicos", enquanto desenvolvedores originários de outras linguagens que usavam mais estilo FP em seus códigos parecem pensar que o código mágico era bastante quando você fez a inclusão dinâmica de arquivos por meio de nomes de arquivos e assim por diante.

Nunca chegamos a uma boa conclusão sobre isso, mais do que as pessoas pensam que o código que parece desconhecido é "mágico" ou difícil de ler. Portanto, é uma boa idéia evitar código que parece desconhecido para outros usuários? Eu acho que depende de onde é usado. Eu evitaria usar expressões no estilo fp (inclusão dinâmica de arquivos e assim por diante) em um método principal (ou partes centrais importantes de aplicativos) em que os dados deveriam ser encapsulados de maneira clara e fácil de ler, intuitiva. Por outro lado, não acho que deva ter medo de empurrar o envelope, os outros desenvolvedores provavelmente aprenderão FP rapidamente se forem confrontados com o código FP e talvez tenham algum bom recurso interno para consultar sobre os problemas.

TL; DR: Evite na parte central de alto nível dos aplicativos (que precisam ser lidos para obter uma visão geral da funcionalidade do aplicativo). Caso contrário, use-o.

Christopher Käck
fonte
Bom ponto de evitar usá-lo no código de nível superior!
kd35a
Sempre achei que falta formalismo e muitas oportunidades na definição de técnicas de programação em diferentes tipos de blocos (como métodos estáticos, métodos públicos, construtores etc.). Boa resposta ... está me fazendo revisitar algumas desses pensamentos.
Sprague 15/05
2

A comunidade C ++ recentemente também recebeu lambda e acredito que eles tenham aproximadamente a mesma pergunta. A resposta pode não ser a mesma, no entanto. O equivalente em C ++ seria:

std::copy_if(measures.begin(), measures.end(), inserter(filter),
  [dur](Item i) { return included(i, dur) } );

Agora std::copynão é novo, e as _ifvariantes também não são novas, mas o lambda é. No entanto, é definido de forma bastante clara no contexto: duré capturado e, portanto, constante, Item ivaria no loop, e a única returndeclaração faz todo o trabalho.

Isso parece aceitável para muitos desenvolvedores de C ++. Porém, não amostrei opiniões sobre lambda de ordem superior e esperaria muito menos aceitação.

MSalters
fonte
Ponto interessante, que pode haver respostas diferentes dependendo da linguagem em que está. Provavelmente relacionado ao post de @Christopher Käck sobre como os codificadores PHP tiveram mais problemas com esse tipo de coisa do que os codificadores Python.
kd35a
0

Poste um trecho de código em um colega desenvolvedor que não seja tão fluente em python e pergunte se ele pode passar 5 minutos verificando o código para ver se ele entende.

Se sim, você provavelmente está pronto para ir. Caso contrário, você deve torná-lo mais claro.

Seu colega poderia ser tolo e não entender algo que deveria ser óbvio? Sim, mas você deve sempre programar de acordo com o KISS.

Talvez seu código seja mais eficiente / bonito / elegante do que uma abordagem mais direta e à prova de idiotas? Então você precisa se perguntar: eu preciso fazer isso? Novamente, se a resposta for não, não faça!

Se depois de tudo isso, você ainda acha que precisa e deseja fazê-lo da maneira FP, faça-o de qualquer maneira. Confie nos seus instintos, eles são mais adequados às suas necessidades do que a maioria das pessoas em qualquer fórum :)

Arnab Datta
fonte
fique à vontade para explicar a razão para o downvote
Arnab Datta