Como os proponentes da Programação Funcional responderiam a essa declaração no Código Completo?

30

Na página 839 da segunda edição, Steve McConnell está discutindo todas as maneiras pelas quais os programadores podem "conquistar a complexidade" em grandes programas. Suas dicas culminam com esta afirmação:

"A programação orientada a objetos fornece um nível de abstração que se aplica a algoritmos e dados ao mesmo tempo , um tipo de abstração que somente a decomposição funcional não fornece".

Juntamente com sua conclusão de que "reduzir a complexidade é indiscutivelmente a chave mais importante para ser um programador eficaz" (mesma página), isso parece um grande desafio à programação funcional.

O debate entre FP e OO é frequentemente enquadrado pelos proponentes do FP em torno das questões de complexidade que derivam especificamente dos desafios de concorrência ou paralelização. Mas a simultaneidade certamente não é o único tipo de complexidade que os programadores de software precisam conquistar. Talvez o foco na redução de um tipo de complexidade a aumente bastante em outras dimensões, de modo que, em muitos casos, o ganho não valha o custo.

Se mudarmos os termos da comparação entre FP e OO de questões específicas como concorrência ou reutilização para o gerenciamento da complexidade global, como seria esse debate?

EDITAR

O contraste que eu queria destacar é que o OO parece encapsular e abstrair da complexidade dos dados e dos algoritmos, enquanto a programação funcional parece encorajadora, deixando os detalhes de implementação das estruturas de dados mais "expostos" ao longo do programa.

Veja, por exemplo, Stuart Halloway (um proponente do Clojure FP) aqui dizendo que "a superespecificação dos tipos de dados" é "consequência negativa do estilo OO idiomático" e favorecendo a conceitualização de um AddressBook como um vetor ou mapa simples em vez de um objeto OO mais rico com propriedades e métodos adicionais (não-vetoriais e não semelhantes a mapl). (Além disso, os proponentes OO e Design Orientado a Domínio podem dizer que a exposição de um Catálogo de Endereços como vetor ou mapa superexpõe os dados encapsulados a métodos irrelevantes ou até perigosos do ponto de vista do domínio).

dan
fonte
3
O +1, apesar de a pergunta ter sido estruturada de maneira antagônica, é uma boa pergunta.
mattnz
16
Como muitos declararam nas respostas, decomposição funcional e programação funcional são dois animais diferentes. Portanto, a conclusão de que "isso parece um grande desafio à programação funcional" está claramente errada, não tem nada a ver com isso.
Fabio Fracassi
5
Claramente, o conhecimento da McConnel nos modernos sistemas de tipos de dados funcionais e nos módulos de primeira classe de primeira ordem é um tanto irregular. Sua afirmação é totalmente sem sentido, uma vez que temos os módulos e functores de primeira classe (consulte SML), digite classes (consulte Haskell). É apenas mais um exemplo de como o modo de pensar OO é mais uma religião do que uma metodologia de design respeitosa. E, a propósito, de onde você tirou essa coisa sobre a concorrência? A maioria dos programadores funcionais não se preocupa com o paralelismo.
SK-logic
6
@ SK-logic Tudo que McConnell disse foi que "a decomposição funcional sozinha" não fornece os mesmos meios de abstração que o OOP, o que me parece uma afirmação bastante segura. Em nenhum lugar ele diz que as linguagens FP não possuem meios de abstração tão poderosos quanto o POO. Na verdade, ele não menciona nenhuma linguagem FP. Essa é apenas a interpretação do OP.
sepp2k
2
@ sepp2k, ok, entendo. Ainda assim, um sistema muito complexo e bem estruturado de estruturas de dados e abstrações de processamento pode ser construído sobre nada além de decomposição funcional para um cálculo lambda quase puro - através da simulação do comportamento dos módulos. Não há necessidade das abstrações OO.
SK-logic

Respostas:

13

Lembre-se de que o livro foi escrito há mais de 20 anos. Para programadores profissionais da época, o FP não existia - estava inteiramente no campo de acadêmicos e pesquisadores.

Precisamos enquadrar a "decomposição funcional" no contexto apropriado do trabalho. O autor não está se referindo à programação funcional. Precisamos vincular isso de volta à "programação estruturada" e à GOTObagunça que veio antes. Se o seu ponto de referência for um FORTRAN / COBOL / BASIC antigo que não possui funções (talvez, se você tiver sorte, obteria um único nível de GOSUB) e todas as suas variáveis ​​forem globais, poderá interromper o programa em camadas de funções é um grande benefício.

OOP é um refinamento adicional desse tipo de 'decomposição funcional'. Não apenas você pode agrupar instruções em funções, mas também agrupar funções relacionadas com os dados em que estão trabalhando. O resultado é um pedaço de código claramente definido que você pode ver e entender (idealmente) sem precisar percorrer toda a sua base de código para descobrir o que mais pode operar nos seus dados.

Sean McSomething
fonte
27

Imagino que os proponentes da programação funcional argumentariam que a maioria das linguagens FP fornece mais meios de abstração do que a "decomposição funcional sozinha" e de fato permitem meios de abstrações comparáveis ​​em poder aos das Linguagens Orientadas a Objetos. Por exemplo, pode-se citar as classes de tipo de Haskell ou os módulos de ordem superior de ML como meio de abstração. Portanto, a declaração (que eu tenho certeza que era sobre orientação a objetos versus programação procedural, não programação funcional) não se aplica a elas.

Também deve ser destacado que FP e OOP são conceitos ortogonais e não mutuamente exclusivos. Portanto, não faz sentido compará-los entre si. Você poderia muito bem comparar "OOP imperativo" (por exemplo, Java) versus "OOP funcional" (por exemplo, Scala), mas a declaração que você citou não se aplicaria a essa comparação.

sepp2k
fonte
10
+1 "Decomposição funcional"! = "Programação funcional". O primeiro baseia-se na codificação sequencial clássica usando estruturas de dados de baunilha sem herança (ou apenas rolada à mão), encapsulamento e polimorfismo. O segundo expressa soluções usando o cálculo lambda. Duas coisas completamente diferentes.
Worrier binário
4
Desculpas, mas a frase "programação processual" teimosamente se recusou a se lembrar mais cedo. "Decomposição funcional" para mim é muito mais indicativo de programação procedural do que programação funcional.
Worrier binário
Sim, você está certo. Eu assumi que a Programação Funcional favorece funções reutilizáveis ​​que operam nas mesmas estruturas de dados simples (listas, árvores, mapas) repetidamente - e realmente afirma que esse é um ponto de venda sobre o OO. Veja Stuart Halloway (um defensor do Clojure FP) aqui dizendo que "a superespecificação de tipos de dados" é "consequência negativa do estilo OO idiomático" e favorecendo a conceituação de um AddressBook como um vetor ou mapa em vez de um objeto OO mais rico com outro (não -vectorish & non-maplike) propriedades e métodos.
dan
O link para a citação de Stuart Halloway: thinkrelevance.com/blog/2009/08/12/…
e
2
@dan Pode ser assim que é feito na linguagem Clojure de tipo dinâmico (não sei, não uso Clojure), mas acho que é perigoso concluir com isso, é assim que é feito no FP em geral. O pessoal da Haskell, por exemplo, parece ser muito grande em tipos abstratos e ocultação de informações (talvez não tanto quanto o pessoal da Java).
sepp2k
7

Acho a programação funcional extremamente útil para gerenciar a complexidade. Porém, você tende a pensar na complexidade de uma maneira diferente, definindo-a como funções que atuam em dados imutáveis ​​em níveis diferentes, em vez de encapsulamento no sentido de POO.

Por exemplo, recentemente escrevi um jogo no Clojure, e todo o estado do jogo foi definido em uma única estrutura de dados imutável:

(def starting-game-state {:map ....
                          :player ....
                          :weather ....
                          :other-stuff ....}

E o loop principal do jogo pode ser definido como a aplicação de algumas funções puras ao estado do jogo em um loop:

 (loop [initial-state starting-game-state]
   (let [user-input (get-user-input)
         game-state (update-game initial-state user-input)]
     (draw-screen game-state)
     (if-not (game-ended? game-state) (recur game-state))))

A função principal chamada é update-game, que executa uma etapa de simulação, considerando um estado anterior do jogo e alguma entrada do usuário, e retorna o novo estado do jogo.

Então, onde está a complexidade? Na minha opinião, foi bem administrado:

  • Certamente, a função update-game faz muito trabalho, mas é construída por si mesma compondo outras funções, de modo que é realmente bastante simples. Depois de descer alguns níveis, as funções ainda são bastante simples, fazendo algo como "adicionar um objeto a um bloco de mapa".
  • Certamente o estado do jogo é uma estrutura de grande volume de dados. Mas, novamente, ele foi criado compondo estruturas de dados de nível inferior. Além disso, são "dados puros", em vez de ter quaisquer métodos incorporados ou definição de classe necessária (você pode pensar nisso como um objeto JSON imutável muito eficiente, se quiser), portanto, há muito pouco clichê.

OOP também pode gerenciar a complexidade por meio do encapsulamento, mas se você comparar isso com o OOP, o funcional abordará algumas vantagens muito grandes:

  • A estrutura de dados do estado do jogo é imutável, portanto, muito processamento pode ser feito facilmente em paralelo. Por exemplo, é perfeitamente seguro ter uma tela de desenho de chamada de renderização em um segmento diferente da lógica do jogo - eles não podem se afetar ou ver um estado inconsistente. Isso é surpreendentemente difícil com um grande gráfico de objetos mutáveis ​​......
  • Você pode tirar uma foto do estado do jogo a qualquer momento. As replays são triviais (graças às estruturas de dados persistentes do Clojure, as cópias ocupam quase nenhuma memória, pois a maioria dos dados é compartilhada). Você também pode executar o update-game para "prever o futuro" para ajudar a IA a avaliar diferentes movimentos, por exemplo.
  • Em nenhum lugar eu precisei fazer trocas difíceis para me encaixar no paradigma OOP, como definir uma hierarquia de classe rígida. Nesse sentido, a estrutura de dados funcionais se comporta mais como um sistema flexível baseado em protótipo.

Por fim, para pessoas interessadas em obter mais informações sobre como gerenciar a complexidade em linguagens funcionais versus linguagens OOP, recomendo fortemente o vídeo do discurso de Rich Hickey, Simple Made Easy (filmado na conferência de tecnologia Strange Loop )

Mikera
fonte
2
Eu acho que um jogo é um dos piores exemplos possíveis para demonstrar os supostos "benefícios" da imutabilidade forçada. As coisas estão mudando constantemente em um jogo, o que significa que você deve reconstruir o estado do jogo o tempo todo. E se tudo é imutável, isso significa que você precisa não apenas reconstruir o estado do jogo, mas tudo no gráfico que contém uma referência a ele, ou que contém uma referência a ele, e assim sucessivamente, até que você esteja reciclando todo o estado. programa com mais de 30 FPS, com toneladas de rotatividade de GC para inicializar! Não há nenhuma maneira que você obter um bom desempenho fora dessa ...
Mason Wheeler
7
É claro que os jogos são difíceis com a imutabilidade - foi por isso que o escolhi para demonstrar que ainda pode funcionar! No entanto, você ficaria surpreso com o que estruturas de dados persistentes podem fazer - a maior parte do estado do jogo não precisa ser reconstruída, apenas o que muda. E com certeza há alguma sobrecarga, mas é apenas um pequeno fator constante. Dê-me um número suficiente de núcleos e eu
vencerei seu
3
@Mason Wheeler: Na verdade, é possível obter um desempenho praticamente igual (e tão bom quanto a mutação) com objetos imutáveis, sem muito GC. O truque do Clojure é usar estruturas de dados persistentes : elas são imutáveis ​​para o programador, mas na verdade são mutáveis. Melhor dos dois mundos.
Joonas Pulakka
4
@quant_dev Mais núcleos são mais baratos que núcleos melhores ... escapistmagazine.com/news/view/…
deworde
6
@quant_dev - não é uma desculpa, é um fato matemático e arquitetônico que é melhor você ter uma sobrecarga constante se puder compensá-la escalando seu desempenho quase linearmente com o número de núcleos. O motivo pelo qual as linguagens funcionais oferecerão desempenho superior é que chegamos ao final da linha para desempenho de núcleo único e tudo será sobre simultaneidade e paralelismo no futuro. abordagens funcionais (e imutabilidade em particular) são importantes para fazer esse trabalho.
Mikera
3

"A programação orientada a objetos fornece um nível de abstração que se aplica a algoritmos e dados ao mesmo tempo, um tipo de abstração que somente a decomposição funcional não fornece".

A decomposição funcional sozinha não é suficiente para criar qualquer tipo de algoritmo ou programa: você também precisa representar os dados. Penso que a afirmação acima pressupõe implicitamente (ou pelo menos pode ser entendida como) que os "dados" no caso funcional são do tipo mais rudimentar: apenas listas de símbolos e nada mais. Programar em tal linguagem obviamente não é muito conveniente. No entanto, muitas, especialmente as novas e modernas linguagens funcionais (ou multiparadigmáticas), como o Clojure, oferecem estruturas de dados avançadas: não apenas listas, mas também strings, vetores, mapas e conjuntos, registros, estruturas - e objetos! - com metadados e polimorfismo.

O enorme sucesso prático das abstrações de OO dificilmente pode ser contestado. Mas é a última palavra? Como você escreveu, os problemas de simultaneidade já são os principais problemas, e o OO clássico não contém nenhuma idéia de simultaneidade. Como resultado, as soluções OO de fato para lidar com a simultaneidade são apenas fitas adesivas sobrepostas: funciona, mas é fácil de estragar, retira uma quantidade considerável de recursos cerebrais da tarefa essencial em questão e não é bem dimensionada. Talvez seja possível tirar o melhor de muitos mundos. É isso que as línguas multiparadigmáticas modernas estão buscando.

Joonas Pulakka
fonte
11
Já ouvi a frase "OO no grande, FP no pequeno" em algum lugar - acho que Michael Feathers citou. Isso significa que o FP pode ser bom para partes específicas de um grande programa, mas, em geral, deve ser OO.
dan
Além disso, em vez de usar o Clojure para tudo, mesmo as coisas que são expressas de maneira mais limpa na sintaxe OO mais tradicional, que tal usar o Clojure para os bits de processamento de dados onde é mais limpo e usar Java ou alguma outra linguagem OO para os outros bits. Programação poliglota em vez de programação multiparadigm com o mesmo idioma para todas as partes do programa. (Mais ou menos como a maioria dos aplicativos da Web usa SQL e OO para diferentes camadas.)?
21412 dan dan
@dan: Use qualquer ferramenta que melhor se adapte ao trabalho. Na programação poliglota, o fator crucial é a comunicação conveniente entre as linguagens, e Clojure e Java dificilmente poderiam jogar melhor juntos . Acredito que os programas Clojure mais substanciais usam pelo menos alguns bits das bibliotecas Java padrão do JDK aqui e ali.
Joonas Pulakka
2

O estado mutável é a raiz da maioria das complexidades e problemas relacionados à programação e ao design de software / sistema.

OO abraça um estado mutável. FP detesta estado mutável.

Tanto o OO quanto o FP têm seus usos e pontos positivos. Escolha sabiamente. E lembre-se do ditado: "Fechamentos são objetos do pobre homem. Objetos são fechamento do pobre homem".

Maglob
fonte
3
Não tenho certeza de que sua afirmação inicial seja verdadeira. A raiz da "maioria" das complexidades? Na programação que fiz ou vi, o problema não é tanto um estado mutável quanto uma falta de abstração e uma superabundância de detalhes através do código.
dan
11
@ Dan: Interessante. Já vi muitas coisas opostas: os problemas decorrem do uso excessivo da abstração, dificultando a compreensão e, quando necessário, a correção dos detalhes do que realmente está acontecendo.
Mason Wheeler
1

A programação funcional pode ter objetos, mas esses objetos tendem a ser imutáveis. Funções puras (funções sem efeitos colaterais) operam nessas estruturas de dados. É possível criar objetos imutáveis ​​em linguagens de programação orientadas a objetos, mas eles não foram projetados para isso e não é assim que costumam ser usados. Isso dificulta o raciocínio sobre programas orientados a objetos.

Vamos dar um exemplo muito simples. Digamos que a Oracle decidiu que o Java Strings deveria ter um método reverso e você escreveu o código a seguir.

String x = "abc";
StringBuffer y = new StringBuffer(x);
y.reverse();
x.reverse();
x.toString().equals(y.toString());

o que a última linha avalia? Você precisa de um conhecimento especial da classe String para saber que isso seria avaliado como falso.

E se eu fiz minha própria classe WuHoString

String x = "abc";
WuHoString y = new WuHoString(x);
y.reverse();
x.reverse();
x.toString().equals(y.toString())

É impossível saber o que a última linha avalia.

Em um estilo de programação funcional, seria escrito da seguinte maneira:

String x;
equals(toString(reverse(x)), toString(reverse(WuHoString(x))))

e deveria ser verdade.

Se uma função em uma das classes mais básicas é tão difícil de raciocinar, então nos perguntamos se a introdução dessa idéia de objetos mutáveis ​​aumentou ou diminuiu a complexidade.

Obviamente, existem todos os tipos de definições do que constitui orientação a objetos e o que significa ser funcional e o que significa ter os dois. Para mim, você pode ter um "estilo de programação funcional" na linguagem que não possui funções como funções de primeira classe, mas outras linguagens são criadas para isso.

WuHoUnited
fonte
3
É engraçado dizer que as linguagens OO não são criadas para objetos imutáveis ​​e, posteriormente, usam um exemplo com strings (que são imutáveis ​​na maioria das linguagens OO, incluindo Java). Também devo salientar que existem linguagens OO (ou melhor, multiparadigma) que são projetadas com ênfase em objetos imutáveis ​​(Scala, por exemplo).
sepp2k
@ sepp2k: Acostume-se a isso. Os defensores da PF estão sempre dando exemplos bizarros e inventados que não têm nada a ver com a codificação do mundo real. É a única maneira de fazer com que os principais conceitos de FP, como imutabilidade forçada, pareçam bons.
Mason Wheeler
11
@Mason: Hein? Não seria a melhor maneira de fazer a imutabilidade parecer "Java (e C #, python, etc) usa cadeias imutáveis ​​e funciona muito bem"?
sepp2k
11
@ sepp2k: Se strings imutáveis ​​funcionam tão bem, por que as classes de estilo StringBuilder / StringBuffer continuam aparecendo por toda parte? É apenas mais um exemplo de uma inversão de abstração no seu caminho.
Mason Wheeler
2
Muitas linguagens orientadas a objetos permitem criar objetos imutáveis. Mas o conceito de vincular os métodos à classe realmente o desencoraja do meu ponto de vista. O exemplo String não é realmente um exemplo artificial. Sempre que eu chamo qualquer método em java, estou arriscando se meus parâmetros serão alterados nessa função.
WuHoUnited
0

Eu acho que na maioria dos casos, a abstração clássica de POO não cobre a complexidade da simultaneidade. Portanto, OOP (pelo seu significado original) não exclui FP, e é por isso que vemos coisas como scala.

duyt
fonte
0

A resposta depende do idioma. Lisps, por exemplo, têm a realmente puro corretamente que o código é de dados - os algoritmos que você escreve são realmente apenas listas Lisp! Você armazena dados da mesma maneira que escreve o programa. Essa abstração é simultaneamente mais simples e mais completa que o OOP e permite que você faça coisas realmente legais (consulte macros).

Haskell (e linguagem semelhante, imagino) tem uma resposta completamente diferente: tipos de dados algébricos. Um tipo de dados algébrico é como uma Cestrutura, mas com mais opções. Esses tipos de dados fornecem a abstração necessária para modelar dados; As funções fornecem a abstração necessária para modelar algoritmos. Classes de tipos e outros recursos avançados fornecem um nível ainda mais alto de abstração sobre ambos.

Por exemplo, estou trabalhando em uma linguagem de programação chamada TPL por diversão. Tipos de dados algébricos torná-la realmente fácil para representar valores:

data TPLValue = Null
              | Number Integer
              | String String
              | List [TPLValue]
              | Function [TPLValue] TPLValue
              -- There's more in the real code...

O que isso diz - de uma maneira muito visual - é que um TPLValue (qualquer valor no meu idioma) pode ser um Nullou um Numbercom um Integervalor ou mesmo um Functioncom uma lista de valores (os parâmetros) e um valor final (o corpo )

Em seguida, posso usar classes de tipo para codificar algum comportamento comum. Por exemplo, eu poderia criar uma TPLValueinstância do Showque significa que pode ser convertida em uma string.

Além disso, posso usar minhas próprias classes de tipo quando precisar especificar o comportamento de certos tipos (incluindo aqueles que não foram implementados por mim). Por exemplo, eu tenho uma Extractableclasse de tipo que me permite escrever uma função que recebe TPLValueae retorna um valor normal apropriado. Assim, extractpode converter a Numberpara um Integerou a Stringpara a Stringenquanto Integere Stringsão instâncias de Extractable.

Finalmente, a principal lógica do meu programa está em várias funções como evale apply. Esses são realmente o núcleo - eles pegam TPLValuese os transformam em mais TPLValues, além de lidar com estados e erros.

No geral, as abstrações que estou usando no meu código Haskell são realmente mais poderosas do que o que eu teria usado em uma linguagem OOP.

Tikhon Jelvis
fonte
Sim, tenho que amar eval. "Ei, olhe para mim! Não preciso escrever minhas próprias falhas de segurança; tenho uma vulnerabilidade de execução de código arbitrária embutida na linguagem de programação!" Conflitar dados com código é a causa raiz de uma das duas classes mais populares de vulnerabilidades de segurança de todos os tempos. Sempre que você vê alguém ser hackeado por causa de um ataque de injeção de SQL (entre muitas outras coisas), é porque algum programador lá fora não sabe como separar corretamente os dados do código.
Mason Wheeler
evalnão depende muito da estrutura do Lisp - você pode ter evalem linguagens como JavaScript e Python. O poder real vem das macros de escrita, que são basicamente programas que atuam em programas como dados e geram outros programas. Isso torna a linguagem muito flexível e cria abstrações poderosas com facilidade.
Tikhon Jelvis
3
Sim, eu ouvi as "macros são incríveis" falarem muitas vezes antes. Mas nunca vi um exemplo real de uma macro Lisp fazendo algo que 1) é prático e algo que você realmente gostaria de fazer no código do mundo real e 2) não pode ser realizado com a mesma facilidade em qualquer linguagem que suporte funções.
Mason Wheeler
11
@MasonWheeler em curto-circuito and. curto-circuito or. let. let-rec. cond. defn. Nada disso pode ser implementado com funções em linguagens de pedidos aplicativas. for(lista de compreensões). dotimes. doto.
11
@ MattFenwick: OK, eu realmente deveria ter acrescentado um terceiro ponto aos meus dois acima: 3) ainda não está embutido em nenhuma linguagem de programação sã. Porque esse é o único exemplo de macro verdadeiramente útil que eu já vi, e quando você diz "Ei, olhe para mim, minha linguagem é tão flexível que eu posso implementar meu próprio curto-circuito and!" Ouço: "olhe para mim, minha linguagem está tão prejudicada que nem sequer provoca um curto-circuito ande eu tenho que reinventar a roda para tudo !"
Mason Wheeler
0

A frase citada não tem mais validade, até onde posso ver.

As linguagens OO contemporâneas não podem abstrair sobre tipos cujo tipo não é *, ou seja, tipos com classificação mais alta são desconhecidos. Seu sistema de tipos não permite expressar a idéia de "algum contêiner com elementos Int, que permite mapear uma função sobre os elementos".

Portanto, essa função básica como Haskells

fmap :: Functor f => (a -> b) -> f a -> f b 

não pode ser escrito facilmente em Java *), por exemplo, pelo menos não de maneira segura. Portanto, para obter a funcionalidade básica, você precisa escrever muitos clichês, porque precisa

  1. um método para aplicar uma função simples aos elementos de uma lista
  2. um método para aplicar a mesma função simples aos elementos de uma matriz
  3. um método para aplicar a mesma função simples aos valores de um hash,
  4. .... set
  5. .... árvore
  6. ... 10. testes de unidade para o mesmo

E, no entanto, esses cinco métodos são basicamente o mesmo código, mais ou menos. Em contraste, em Haskell, eu precisaria de:

  1. Uma instância do Functor para lista, matriz, mapa, conjunto e árvore (geralmente predefinida ou pode ser derivada automaticamente pelo compilador)
  2. a função simples

Observe que isso não vai mudar com o Java 8 (apenas se pode aplicar funções mais facilmente, mas, exatamente, o problema acima se materializará. Contanto que você nem sequer tenha funções de ordem superior, é provável que nem mesmo capaz de entender para que servem os tipos mais elevados.)

Mesmo as novas linguagens OO, como o Ceilão, não têm tipos mais elevados. (Perguntei a Gavin King recentemente, e ele me disse que não era importante no momento.) Mas não sei sobre Kotlin.

*) Para ser justo, você pode ter uma interface Functor que possui um método fmap. O ruim é que você não pode dizer: Ei, eu sei como implementar o fmap para a classe de biblioteca SuperConcurrentBlockedDoublyLinkedDequeHasMap, caro compilador, por favor, aceite que, a partir de agora, todos os SuperConcurrentBlockedDoublyLinkedDequeHasMaps são Functors.

Ingo
fonte
FTR: o typechecker do Ceilão e o back-end do JavaScript agora fazer apoio maior handed tipos (e também mais elevados classificar os tipos). É considerado um recurso "experimental". No entanto, nossa comunidade tem se esforçado para encontrar aplicativos práticos para essa funcionalidade, por isso é uma questão em aberto se isso será uma parte "oficial" do idioma. Eu não esperar que seja suportado pelo Java backend em algum momento.
Gavin King
-2

Qualquer pessoa que já tenha programado no dBase saberia como as macros de linha única foram úteis para criar código reutilizável. Embora eu não tenha programado no Lisp, li de muitos outros que juram por macros de tempo de compilação. A idéia de injetar código no código em tempo de compilação é usada de forma simples em todos os programas em C com a diretiva "include". Como o Lisp pode fazer isso com um programa Lisp e porque o Lisp é altamente reflexivo, você inclui inclusões muito mais flexíveis.

Qualquer programador que apenas pegue uma string de texto arbitrária da Web e a repasse para o banco de dados não é um programador. Da mesma forma, qualquer pessoa que permita que os dados do "usuário" se tornem automaticamente código executável é obviamente estúpida. Isso não significa que permitir que os programas manipulem dados no momento da execução e depois os executem como código é uma má idéia. Acredito que esta técnica será indispensável no futuro, e que terá um código "inteligente" que realmente escreverá a maioria dos programas. Todo o "problema de dados / código" ou não é uma questão de segurança no idioma.

Um dos problemas com a maioria dos idiomas é que eles foram criados para uma única pessoa off-line executar algumas funções por conta própria. Os programas do mundo real exigem que muitas pessoas tenham acesso o tempo todo e ao mesmo tempo a partir de múltiplos núcleos e vários clusters de computadores. A segurança deve fazer parte do idioma, e não do sistema operacional, e em um futuro não muito distante.

David Clark
fonte
2
Bem-vindo aos programadores. Considere atenuar a retórica em sua resposta e apoiar algumas de suas reivindicações com referências externas.
11
Qualquer programador que permita que os dados do usuário se tornem automaticamente código executável é obviamente ignorante . Não é estúpido. Fazer isso dessa maneira geralmente é fácil e óbvio, e se eles não sabem por que é uma má idéia e se existe uma solução melhor, você não pode culpá-los por fazê-lo. (Qualquer um que mantém fazê-lo depois de terem sido ensinados que há uma maneira melhor, no entanto, é obviamente estúpido.)
Mason Wheeler