Linguagem (s) de programação funcional mais pura? [fechadas]

20

Estou interessado em aprender melhor a programação funcional. Para fazer isso, parece óbvio que devo me forçar a usar a linguagem de programação funcional mais pura possível. Por isso, estou aqui pedindo, mais ou menos, um pedido de linguagens de programação funcionais de acordo com sua pureza.

Parece-me que seria mais prático aprender Lisp ou Clojure (ou Scheme, ou Scala etc.), mas pelo que ouvi recentemente, seria muito difícil vencer Haskell ao ensinar princípios de programação funcional a alguém. Ainda não tenho certeza disso, então estou perguntando: qual é a linguagem de programação funcional mais pura? Um pedido seria ótimo se vários estivessem competindo pelo magnífico título da mais pura linguagem de programação funcional.

Joanis
fonte
2
Eu aprendi Miranda na universidade, por isso sou preconceituosa, mas sugiro Haskel a quem quiser mergulhar em uma linguagem funcional sem as distrações da impureza . * 8 ')
Mark Booth
2
Depois de concluir a aprendizagem da programação funcional, você também deve aprender a programação de tipo estaticamente com um sistema de tipo expressivo. Na categoria combinada (funcional e digitada), sugiro: Coq> Haskell> OCaml> Scala> others. Existem algumas alternativas menos populares que se encaixam entre Coq e Haskell (como Epigram e Agda). Haskell sente falta do expressivo sistema de módulos do OCaml.
11783 lukstafi

Respostas:

28

Não há escala para avaliar o grau de pureza das linguagens funcionais. Se a linguagem permite efeitos colaterais, é impura; caso contrário, é pura. Por essa definição, Haskell, Mercury, Clean etc são linguagens funcionais puras; enquanto Scala, Clojure, F #, OCaml etc são impuros.

EDIT: Talvez eu devesse ter formulado isso como "se a linguagem não permitir efeitos colaterais sem informar o sistema de tipos , é puro. Caso contrário, é impuro".

desaparecido
fonte
6
Não é bem assim: Haskell permite efeitos colaterais ( IOmônada). É que o código causador de efeitos colaterais está claramente marcado como tal. Eu não acho que seja útil falar em linguagens puras / impuras (Haskell permite programar imperativamente! Suspiro!), Mas ainda pode ser útil falar em pedaços de código (função, módulo, programa, o que for) como puros /impuro.
Frank Shearar
8
@ Frank: Sim, Haskell permite efeitos colaterais, mas faz mais do que apenas marcar códigos que causam efeitos colaterais. Ele também mantém a transparência referencial, mesmo ao usar código causador de efeito colateral e é isso que ele torna puro - pelo menos pela definição de pureza de muitas pessoas. É claro que isso não corresponde à definição de "falta de efeito colateral permitido" de missingfaktor.
sepp2k
4
@Frank Shearar, mas é útil falar dessa maneira, porque a IOmônada é pura. É a biblioteca de tempo de execução que não é, não é seu código, pois sua mainfunção é basicamente um enorme transformador de estado. (Algo como main :: World -> Worldnos bastidores)
alternativa
1
Meu idioma também tem transparência referencial. Você acabou de escrever program = "some C code"e, em seguida, o ambiente de tempo de execução lida com o código C. :-)
Daniel Lubarov
3
Como a impressão na tela é um efeito colateral, programas funcionais realmente puros são bastante chatos.
15

Como aprender é o seu objetivo, e não escrever programas por si só, você não pode obter nada mais puro do que o Lambda Calculus .

O Cálculo Lambda existia antes dos computadores serem inventados. Foram necessários vários lógicos habilidosos trabalhando para descobrir como fazer a subtração (por um tempo foi teorizado que apenas a adição e a multiplicação eram possíveis).

Aprender como booleanos e números e ifpode ser inventado a partir de aparentemente nada não colocará mais gás em seu tanque, mas fará com que seu tanque seja muito maior.

Macneil
fonte
É verdade que provavelmente não há nada mais puro do que isso, mas estamos atravessando a barreira da matemática um pouco demais. Eu ainda quero praticar programação e aprender uma nova linguagem, absorvendo os princípios funcionais. No entanto, concordo que pode ser interessante estudar o cálculo lambda apenas para melhor entender os fundamentos do paradigma funcional (e ter um tanque maior).
Joanis
2
Escrever uma prova está escrevendo um programa e escrever um programa está escrevendo uma prova. Quando você aprender o isomorfismo Curry-Howard, perceberá que ultrapassou essa barreira matemática dez mil linhas atrás.
Macneil
1
Não existe uma representação simples de -1.
Daniel Lubarov
2
@Mason Wheeler: o cálculo λ sozinho não possui números ou realmente qualquer tipo de dado além das abstrações lambda. A menos que especificado de outra forma, qualquer um que fale sobre números no cálculo λ provavelmente significa a codificação da Igreja para números naturais , onde um número n é representado pela composição da função n vezes mais. Dito isto, eu sou duvidosa era de que uma luta muito para descobrir subtração uma vez que alguém realmente tentei; Eu trabalhei sozinho em uma tarde, considerando apenas a definição de adição e conhecimento de que a subtração era possível.
CA McCann
1
Subtração usando números da Igreja é fácil quando você tem decomposição por casos. Construir todo o cálculo (derivado) para suportar números negativos, mais trabalho.
Donal Fellows
6

As linguagens impuras realmente não diferem em princípio das linguagens imperativas mais familiares, especialmente agora que muitos truques funcionais foram copiados. O que é diferente é o estilo - como você resolve problemas.

Se você considera Haskell puro, ou conta a mônada de IO como impureza, o estilo Haskell é uma forma extrema desse estilo e vale a pena aprender.

A mônada Haskell IO é derivada da teoria matemática das mônadas (é claro). No entanto, para programadores imperativos, acho que uma maneira inversa de chegar às mônadas faz mais sentido.

Fase um - uma linguagem funcional pura pode facilmente retornar um grande valor de string como resultado. Essa grande cadeia pode ser o código-fonte de um programa imperativo, derivado de maneira funcional pura de alguns parâmetros de especificação de requisitos. Você pode criar um compilador de "nível superior" que executa seu gerador de código e, em seguida, alimenta automaticamente esse código gerado no compilador de linguagem imperativa.

Fase dois - em vez de gerar código fonte textual, você gera uma árvore de sintaxe abstrata fortemente tipada. Seu compilador de linguagem imperativa é absorvido pelo compilador "de nível superior" e aceita o AST diretamente como código-fonte. Isso é muito mais próximo do que Haskell faz.

Isso ainda é estranho, no entanto. Por exemplo, você tem dois tipos distintos de funções - aquelas avaliadas durante a fase de geração de código e as executadas quando o programa gerado é executado. É um pouco como a distinção entre funções e modelos em C ++.

Portanto, para a fase 3, torne os dois iguais - a mesma função com a mesma sintaxe pode ser parcialmente avaliada durante a "geração de código", ou totalmente avaliada, ou não avaliada. Além disso, descarte todos os nós AST da construção em loop em favor da recursão. De fato, descarte a ideia de nós AST como um tipo especial de dados - não tenha nós AST de "valor literal", apenas valores etc.

Isso é basicamente o que a mônada de E / S faz - o operador de ligação é uma maneira de compor "ações" para formar programas. Não é nada de especial - apenas uma função. Muitas expressões e funções podem ser avaliadas durante a "geração de código", mas aquelas que dependem dos efeitos colaterais de E / S devem ter a avaliação atrasada até o tempo de execução - não por nenhuma regra especial, mas como uma consequência natural das dependências de dados em expressões.

Mônadas em geral são apenas generalizações - elas têm a mesma interface, mas implementam as operações abstratas de maneira diferente; portanto, em vez de avaliar uma descrição do código imperativo, elas avaliam para outra coisa. Ter a mesma interface significa que há algumas coisas que você pode fazer com as mônadas sem se importar com a mônada, o que acaba sendo útil.

Essa descrição sem dúvida fará explodir cabeças de puristas, mas para mim explica algumas das reais razões pelas quais Haskell é interessante. Ele obscurece a fronteira entre programação e metaprogramação e usa as ferramentas de programação funcional para reinventar a programação imperativa sem precisar de sintaxe especial.

Uma crítica que tenho aos modelos C ++ é que eles são uma espécie de sublanguagem funcional pura e quebrada em uma linguagem imperativa - para avaliar a mesma função básica em tempo de compilação, em vez de em tempo de execução, é necessário reimplementá-la usando um estilo completamente diferente de codificação. Em Haskell, embora a impureza deva ser rotulada como tal em seu tipo, a mesma função exata pode ser avaliada no sentido de metaprogramação e no sentido de não metaprogramação em tempo de execução no mesmo programa - não há linha dura entre programação e metaprogramação.

Dito isto, existem algumas coisas de metaprogramação que Haskell padrão não pode fazer, basicamente porque tipos (e talvez algumas outras coisas) não são valores de primeira classe. Existem variantes de idioma que tentam resolver isso, no entanto.

Muitas coisas que eu disse sobre Haskell podem ser aplicadas em linguagens funcionais impuras - e às vezes até em linguagens imperativas. Haskell é diferente porque você não tem escolha a não ser adotar essa abordagem - basicamente obriga você a aprender esse estilo de trabalho. Você pode "escrever C em ML", mas não pode "escrever C em Haskell" - pelo menos não sem aprender o que está acontecendo sob o capô.

Steve314
fonte
Obrigado! Eu queria saber se a genicidade dos modelos C ++ poderia ter sido unificada em funções próprias. Parece que Haskell faz isso, mas a parte importante é se isso pode ser implementado em uma linguagem compilada , de modo que o mesmo mecanismo que cria funções genéricas a partir de modelos durante o tempo de compilação também possa avaliá-las durante o tempo de execução ...
Milind R
5

Pessoalmente, categorizo ​​os idiomas em três níveis de pureza funcional:

  • Linguagens funcionais puras - ou seja, aquelas que tratam todo o programa como uma função pura e manipulam a mutabilidade apenas por meio da interação com o tempo de execução - Haskell é provavelmente o exemplo canônico

  • Linguagens funcionais impuras - ou seja, aquelas que enfatizam um estilo funcional, mas permitem efeitos colaterais. O Clojure está claramente nessa categoria (permite a mutação de maneira controlada como parte de sua estrutura STM), também OCaml ou F #

  • Linguagens multiparadigmas - essas não são linguagens funcionais em primeiro lugar, mas podem suportar um estilo funcional pelo uso de funções de primeira classe etc. Scala é um bom exemplo aqui, eu também colocaria o Common Lisp nessa categoria e você pode até incluir idiomas como JavaScript .

Na sua situação, sugiro aprender Haskell primeiro, depois Clojure. Foi isso que fiz e funcionou muito bem para mim! Haskell é bonito e ensina os princípios funcionais mais puros, Clojure é muito mais pragmático e ajuda a fazer muito enquanto ainda é muito funcional no coração.

Na verdade, não conto a terceira categoria como linguagens funcionais (embora depois de aprender Haskell e Clojure, muitas vezes eu me aproveitei das técnicas funcionais ao usá-las!)

Mikera
fonte
Dentro de limitações muito rígidas , até C possui recursos funcionais. (A principal limitação é a síntese de execução de funções, o que é verdadeiramente horrível em C, tanto assim que a maioria das pessoas afirmam que não pode ser feito.)
Donal Fellows
2
@Donal Fellows: No limite em que a bobagem chega ao infinito, toda linguagem Turing-completa é funcional: basta implementar um intérprete Lisp na linguagem e depois usá-lo. :]
CA McCann
Paradigmas não têm sentido se você considerar que tudo está completo em Turing e, portanto, pode imitar todo o resto. Quando você pensa em paradigmas, precisa se concentrar no que a linguagem torna idiomática e no que desencoraja / impede. Para contar como uma linguagem funcional, a meu ver, a mutação e os efeitos colaterais devem ser unidiomatic (para FP impuros) ou proibidos (para FP puro). Isso significa que C não é funcional. Tampouco são linguagens multiparadigma como o Scala.
Mikera
@mikera: Acho sua resposta muito interessante: se o Scala não é funcional, mas apenas com vários paradigmas, como você avalia os recursos de programação funcional em C ++ 11, C # ou até Java (a introdução planejada de lambdas)?
Giorgio
@Giorgio: Eu acho que os recursos funcionais em todas as linguagens mencionadas são provavelmente uma boa idéia, eles simplesmente não as tornam "linguagens funcionais". Ou, olhando para a direção oposta, o fato de você poder fazer E / S monádica no estilo imperativo não faz do Haskell uma linguagem imperativa :-). Linguagens multiparadigmas são basicamente o resultado quando você reúne recursos de muitos paradigmas diferentes e não coloca nenhum estilo específico em primeiro lugar. O IMHO C ++, C # e Java estão todos se movendo no sentido de serem multiparadigmas, mas ainda não chegaram lá, já que o OOP baseado em classe ainda é dominante.
mikera
3

Se uma linguagem funcional pura é tal, que possui apenas funções puras (rotinas que não têm efeitos colaterais), então é um pouco inútil, porque ela não pode ler entrada nem gravar saída;)

Porque isso é realmente para aprender, acho que o isolamento não é necessariamente o caminho a percorrer. A programação funcional é um paradigma. É importante entender quais paradigmas são adequados para quais problemas e, mais importante, como eles podem ser melhor combinados.

Vou dizer agora: a moda de programação é estúpida e contraproducente. As únicas coisas importantes são que seu programa é curto, fácil de escrever, fácil de manter e funciona corretamente. Como você consegue isso não tem nada a ver com modismos de programação. - Richard Jones

Fora isso, se você está procurando "pureza", pode querer dar uma olhada no Pure . Note, no entanto, a extrema facilidade de chamar as rotinas C torna funcionalmente impuro (mas também muito poderoso).

back2dos
fonte
3

Não é uma resposta totalmente séria, mas Unlambda tem que ser um candidato. Você não pode ser mais "puro funcional" do que os combinadores de SKI.

Stephen C
fonte
4
Loucura! Heresia! Que tipo de linguagem pura tem absurdos como combinadores com efeitos colaterais? Não não. O que você realmente quer aqui é Lazy K .
CA McCann
2

Erlang, Haskell, Scheme, Scala, Clojure e F #

Esta questão seria provavelmente melhor que você ajudá-lo em sua busca como bem .

dustyprogrammer
fonte
Obrigado, pergunta interessante de fato. Eu comecei com o PLT-Scheme / Racket, mas ainda não vi o SICP ... O mundo real Haskell também me parece muito interessante.
Joanis
Esquema incentiva um estilo funcional, mas tem set!(entre outras coisas) ...
Daniel Lubarov
Sugiro OCaml, porque é o meu favorito. E possui um sistema de módulos que Haskell e F # sentem falta.
11783 lukstafi
@lukstafi talvez o haskell tenha mudado nos últimos 3 anos (eu sei que cresceu como um louco), mas definitivamente existem módulos no haskell.
Sara
@kai Por um sistema de módulos, eu quis dizer módulos parametrizados por outros módulos (um "cálculo lambda" de módulos).
lukstafi