Há um artigo clássico chamado Sobre os critérios a serem usados na decomposição de sistemas em módulos que acabei de ler pela primeira vez. Faz todo o sentido para mim e é provavelmente um daqueles artigos nos quais o OOP foi baseado. Sua conclusão:
Tentamos demonstrar por esses exemplos que quase sempre é incorreto iniciar a decomposição de um sistema em módulos com base em um fluxograma. ... Cada módulo é projetado para ocultar tal decisão dos outros
Na minha opinião sem instrução e inexperiente, a programação funcional segue exatamente o conselho oposto deste artigo. Meu entendimento é que a programação funcional torna o fluxo de dados idiomático. Os dados são transmitidos de função para função, cada função sendo intimamente ciente dos dados e "alterando-os" ao longo do caminho. E acho que vi um Rich Hickey falar onde ele fala sobre como a ocultação de dados é superestimada ou desnecessária ou algo assim, mas não me lembro com certeza.
- Primeiro, quero saber se minha avaliação está correta. O paradigma do PF e este artigo discordam filosoficamente?
- Supondo que discordem, como o FP "compensa" a falta de ocultação de dados? Talvez eles sacrificem a ocultação de dados, mas ganhem X, Y e Z. Gostaria de saber o motivo pelo qual X, Y e Z são considerados mais benéficos do que a ocultação de dados.
- Ou, supondo que discordem, talvez a FP pense que a ocultação de dados é ruim. Se sim, por que acha que a ocultação de dados é ruim?
- Supondo que eles concordem, eu gostaria de saber o que é a implementação dos FPs de ocultação de dados. É óbvio ver isso no OOP. Você pode ter um
private
campo que ninguém fora da classe possa acessar. Não há analogia óbvia disso para mim no FP. - Sinto que há outras perguntas que devo fazer, mas não sei. Sinta-se à vontade para responder também.
Atualizar
Eu encontrei essa palestra de Neal Ford que tem um slide muito relevante. Vou incorporar a captura de tela aqui:
fonte
Respostas:
O artigo mencionado é sobre modularidade em geral e se aplicaria igualmente a programas estruturados, funcionais e orientados a objetos. Já ouvi falar desse artigo antes de alguém que era um grande sujeito de OOP, mas li como um artigo sobre programação em geral, não como algo específico de OOP. Há um artigo famoso sobre programação funcional, Why Functional Programming Matters , e a primeira frase da conclusão declara "Neste artigo, argumentamos que a modularidade é a chave para uma programação bem-sucedida". Portanto, a resposta para (1) é não.
Funções bem projetadas não assumem mais seus dados do que precisam, portanto a parte sobre "intimamente ciente dos dados" está errada. (Ou pelo menos tão errado quanto seria no OOP. Você não pode programar estritamente em um alto nível de abstração e ignorar todos os detalhes para sempre em qualquer paradigma. No final, alguma parte do programa realmente precisa saber sobre o detalhes específicos dos dados.)
Ocultar dados é um termo específico de POO e não é exatamente o mesmo que o oculto de informações discutido no artigo. As informações ocultas no artigo são sobre decisões de design difíceis de tomar ou que podem mudar. Nem todas as decisões de design sobre um formato de dados são difíceis ou podem mudar, e nem todas as decisões que são difíceis ou que podem mudar são sobre um formato de dados. Pessoalmente, não consigo entender por que os programadores de OO querem que tudo seja um objeto. Às vezes, basta uma estrutura de dados simples.
Edit: Encontrei uma citação relevante de uma entrevista com Rich Hickey .
fonte
Na verdade, não, mas isso contribuiu para a discussão, especialmente para os profissionais que, na época, eram treinados para decompor sistemas usando os primeiros critérios que ele descreve no artigo.
Não. Além disso, a meu ver, sua descrição de como é um programa FP não é diferente de nenhum outro que utilize procedimentos ou funções:
... exceto na parte "intimidade", já que você pode (e geralmente possui) funções operando em dados abstratos, precisamente para evitar a intimidade. Assim, você tem algum controle sobre essa "intimidade" e pode regulá-la como quiser, configurando interfaces (ou seja, funções) para o que deseja ocultar.
Portanto, não vejo razão para não sermos capazes de seguir os critérios de ocultação de informações de Parnas usando programação funcional e terminar com a implementação de um índice KWIC com benefícios semelhantes apontados como sua segunda implementação.
No que diz respeito aos dados, é possível elaborar abstrações de dados e abstrações de tipo de dados usando o FP. Qualquer um desses oculta estruturas e manipulações concretas dessas estruturas de concreto usando funções como abstrações.
EDITAR
Há um número crescente de afirmações aqui afirmando que "ocultar dados" no contexto do FP não é tão útil (ou OOP-ish (?)). Então, deixe-me carimbar aqui um exemplo muito simples e claro do SICP:
Suponha que seu sistema precise trabalhar com números racionais. Uma maneira de representá-los é como um par ou uma lista de dois números inteiros: o numerador e o denominador. Portanto:
Se você ignorar a abstração de dados, provavelmente obterá o numerador e o denominador usando
car
ecdr
:Seguindo essa abordagem, todas as partes do sistema que manipulam números racionais saberão que um número racional é um
cons
- elascons
numerarão para criar racionais e extraí-las usando operadores de lista.Um problema que você pode enfrentar é quando precisa ter uma forma reduzida dos números racionais - serão necessárias alterações em todo o sistema. Além disso, se você decidir reduzir no momento da criação, poderá descobrir mais tarde que reduzir ao acessar um dos termos racionais é melhor, gerando outra alteração em escala total.
Outro problema é se, hipoteticamente, uma representação alternativa é preferida e você decide abandonar a
cons
representação - mudança em escala completa novamente.Qualquer esforço sensato para lidar com essas situações provavelmente começará a esconder a representação dos racionais por trás das interfaces. No final, você pode acabar com algo assim:
(make-rat <n> <d>)
retorna o número racional cujo numerador é o número inteiro<n>
e cujo denominador é o número inteiro<d>
.(numer <x>)
retorna o numerador do número racional<x>
.(denom <x>)
retorna o denominador do número racional<x>
.e o sistema não saberá mais (e não deve mais) saber do que são feitos os racionais. Isto porque
cons
,car
ecdr
não são intrínsecos à racionais, masmake-rat
,numer
edenom
são . Obviamente, isso poderia ser facilmente um sistema de FP. Portanto, "ocultação de dados" (neste caso, mais conhecido como abstração de dados ou o esforço de encapsular representações e estruturas concretas) surge como um conceito relevante e uma técnica amplamente utilizada e explorada, seja no contexto de OO, programação funcional ou tanto faz.E o ponto é ... embora se possa tentar fazer distinções entre que "tipo de ocultação" ou encapsulamento eles estão fazendo (estejam escondendo uma decisão de design, estruturas de dados ou algoritmos - no caso de abstrações processuais), todos eles têm o mesmo tema: são motivados por um ou mais pontos que Parnas explicitou. Isso é:
O exemplo acima foi retirado do livro do SICP, portanto, para a discussão e apresentação completa desses conceitos no livro, eu recomendo verificar o capítulo 2 . Eu também recomendo familiarizar-se com tipos de dados abstratos no contexto do FP, o que traz outros problemas para a tabela.
fonte
Sua crença de que a programação funcional carece de ocultação de dados está errada. É preciso apenas uma abordagem diferente para ocultar dados. Uma das maneiras mais comuns de ocultar dados na programação funcional é através do uso de funções polimórficas que assumem uma função como argumento. Por exemplo, esta função
só pode ver a estrutura mais externa dos dados (ou seja, que é uma lista), não pode ver nada sobre os dados que a lista contém e só pode operar com os dados através da função única que é passada a ele.
A função que é passada como argumento é análoga a um método público no tipo de dados que a lista contém. Ele fornece uma maneira limitada de operar com os dados, mas não expõe o funcionamento interno do tipo de dados.
fonte
Vou atacar aqui e dizer que o conceito simplesmente não é relevante no FP da maneira como é no OO.
tl; dr; O objetivo da ocultação de dados é garantir que as responsabilidades sejam mantidas onde deveriam estar, e você não tem atores externos mexendo com dados que eles não conhecem. No FP, os dados são gerados por expressões e, dessa maneira, você não pode mexer com os dados, porque não são propriedades mutáveis, mas sim computações composíveis, que alteram completamente as regras do jogo.
Nas minhas experiências com FP; que são reconhecidamente insignificantes, tenho a tendência de encontrar um forte contraste com o OO no que denota boa / comum modelagem de dados.
Esse contraste é que, em OO em geral, você modela as coisas para representar seus dados. Analogia obrigatória do carro:
OO
O ponto a ser observado aqui é que quando você está modelando itens em um formato OO, trata-se de representar itens como Dados. Você tem objetos com propriedades, muitas dessas propriedades são objetos com mais propriedades. Você tem alguns métodos aqui e ali anexados a esses objetos, mas tudo o que eles realmente fazem é geralmente agitar as propriedades dos objetos desta maneira e daquela; novamente, é uma modelagem muito centrada em dados; ou seja, você modela seus dados para interagir com o foco na estruturação para disponibilizar todos os pontos de seus dados, para que os consumidores possam alterar os dados dessa maneira e daquilo.
FP
A grande diferença entre OO e FP que constantemente me impressiona é como eu disse acima da maneira como você modela os dados. No OO, como mencionado acima, você modela dados como dados, no FP, você modela dados como cálculos, expressões, algoritmos, trata-se mais da modelagem das atividades de seus dados do que dos fatos. Pense na modelagem de dados básica em matemática, é sempre sobre obter uma equação que pode gerar seus dados, que modela seus dados como a atividade que os causa, ao contrário da OO, a modelagem está criando uma maneira de representar os dados que você possui. Essa é grande parte da distinção entre FP e OO.
Lembre-se, por muito tempo, o LISP, uma das linguagens fundamentais do FP, viveu com uma quantidade muito pequena de tipos de dados primitivos. Isso funciona porque a abordagem não é modelar representações complexas de seus dados, mas sim computações que geram e expressam os comportamentos do seu sistema.
Quando começo a escrever algum código no FP, começo escrevendo um código que faz alguma coisa, enquanto que quando começo a escrever um código no OO, começo escrevendo modelos que descrevem alguma coisa. O fazer das coisas é oculto no FP por ser expressões, o fazer das coisas é exposto no OO ao ser descrito com dados, ocultar esses dados limita a referida exposição.
Voltando à pergunta em questão, o que a FP diz sobre a ocultação de dados, ela aprecia ou discorda dela ou não?
Eu digo que isso não importa, no OO seus dados são as entranhas e as partes importantes do seu programa que devem ser escondidas de serem intrometidas. No FP, as entranhas e o conhecimento do seu sistema estão todos ocultos nos algoritmos e cálculos que expressam o sistema. Essas são, por definição, mais ou menos imutáveis, a única maneira de modificar as expressões de computação são coisas como macros, mas mesmo assim, as definições de mutações são expressões em si que não podem ser mais intrometidas.
fonte
Há um pouco de paradoxo aqui. Embora a programação funcional se concentre em, bem, funções e frequentemente tenha funções que funcionam diretamente em tipos de dados primitivos, ela tende a ter mais dados ocultos do que a programação orientada a objetos.
Como é isso? Pense em uma interface OO agradável que oculte dados subjacentes - talvez coleções (estou tentando escolher algo quase onipresente). Talvez você não precise conhecer o tipo subjacente dos objetos na coleção ou o tipo de objeto que implementa a coleção, desde que saiba que a coleção é implementada, por exemplo, IEnumerable. Então você tem ocultação de dados.
Na programação funcional, você pode escrever uma função que funcione efetivamente com uma interface IEnumerable, mas opere em um tipo de dados primitivo (ou em qualquer tipo de dado). Mas e se o tipo nunca implementasse os métodos IEnumerable? Aqui está a chave: você sempre pode ter os "métodos" que formam as partes necessárias da "interface" como parâmetros passados para sua função. Ou você pode juntar funções com dados e fazer coisas de maneira OO.
Observe que, de qualquer forma, você não possui menos dados ocultos do que no OO. Minha função geral que funciona em qualquer tipo claramente não está acessando os dados nesse tipo - isso acontece nas funções passadas como parâmetros para a função geral, mas a função geral nunca espia dentro dessas funções para ver os dados.
Portanto, no que diz respeito ao seu ponto 1, não acho que o FP e o artigo realmente discordem. Eu não acho que sua caracterização de FP não oculte dados esteja correta. Alguém poderia implementar o design que o autor preferia no FP, certamente.
Quanto ao ponto 4 (2 e 3 não faz sentido responder, dado o que eu disse para o ponto 1), isso varia. Também varia nos idiomas OO e, em muitos campos particulares, é privado por convenção, e não imposto pelo idioma.
fonte
Primeiro, obrigado pelo link para este excelente artigo, eu não sabia disso até agora, e isso me deu uma grande contribuição sobre algumas coisas que eu estava discutindo com outros designers de software da comunidade nos últimos anos. Aqui está a minha opinião sobre isso:
O design do FP se concentra muito no fluxo de dados (o que é IMHO não tão ruim quanto o artigo pode sugerir). Se isso é um "desacordo" completo, é discutível.
IMHO não compensa. Ver abaixo.
Eu não acho que a maioria dos usuários ou designers de FP se sintam ou pensem dessa maneira, veja abaixo.
Aqui está o ponto - você provavelmente já viu tantos sistemas OOP implementados de uma maneira não funcional que acredita que o OOP não é funcional. E isso é uma falácia, IMHO OOP e FP são conceitos ortogonais e você pode criar perfeitamente sistemas OO funcionais, o que fornece uma resposta óbvia à sua pergunta. A implementação clássica de "objeto" no FP é feita utilizando fechamentos , e se você deseja que objetos sejam usados em um sistema funcional, o ponto principal é projetá-los imutáveis.
Portanto, para criar sistemas maiores, IMHO, você pode criar módulos, classes e objetos usando os conceitos de OO, exatamente da maneira descrita em "Modularização 2" no artigo, sem sair do "caminho FP". Você usará o conceito de módulo da sua linguagem FP favorita, tornará imutáveis todos os seus objetos e usará o "melhor dos dois mundos".
fonte
TL; DR : Não
O paradigma do FP e este artigo discordam filosoficamente ?.
Não, não faz. A Programação Funcional é declarativa, que é "um estilo de construção da estrutura e dos elementos dos programas de computador, que expressa a lógica de uma computação sem descrever seu fluxo de controle". É menos sobre seguir o fluxograma e mais sobre como criar as regras que permitem que o fluxo surja por si só.
A programação procedural está muito mais próxima de uma codificação de um fluxograma do que a programação funcional. Daqui resulta que as transformações que ocorrem e codificam essas transformações em procedimentos executados em ordem, exatamente como o fluxo em um fluxograma descreve.
Ocultar dados
fonte