Como as linguagens imperativas são mais diferentes uma da outra do que as linguagens funcionais?

17

Estou lendo A Implementação de Linguagens de Programação Funcional, de Simon Peyton Jones, e há uma declaração que me surpreendeu um pouco (na página 39):

Em uma extensão muito maior do que é o caso das linguagens imperativas, as linguagens funcionais são amplamente variações sintáticas umas das outras, com relativamente poucas diferenças semânticas.

Agora, isso foi escrito em 1987 e meus pensamentos sobre esse assunto podem ser influenciados por linguagens de programação mais modernas que não existiam ou eram populares na época. No entanto, acho isso um pouco difícil de acreditar. Por exemplo, eu acho que a linguagem de programação Miranda descrita (um antecessor de Haskell) tem semântica muito mais diferente em comparação com uma linguagem estrita como ML do que dizer que C tem para Pascal ou talvez C tem que falar em pequenos detalhes (embora eu ceda que C ++ fornece alguma validação de seu ponto :-).

Mas, novamente, estou baseando isso no meu entendimento intuitivo. Simon Peyton Jones está bastante correto ao dizer isso, ou esse é um ponto controverso?

Jason
fonte

Respostas:

19

Simon está basicamente correto, do ponto de vista extensional. Sabemos muito bem o que são as semânticas das linguagens funcionais modernas, e elas realmente são variações relativamente pequenas uma da outra - cada uma representa traduções ligeiramente diferentes para uma metalinguagem monádica. Mesmo uma linguagem como Scheme (uma linguagem imperativa de ordem superior dinamicamente digitada e com controle de primeira classe) possui uma semântica bastante próxima da ML e Haskell.

V

Mas, para chegar a uma categoria adequada para interpretar linguagens funcionais digitadas modernas, as coisas ficam bastante assustadoras. Basicamente, você acaba construindo uma categoria enriquecida por ultrametria de relações de equivalência parcial sobre esse domínio. (Como exemplo, consulte "Semântica de realizabilidade do polimorfismo paramétrico, referências gerais e tipos recursivos de Birkedal, Stovring e Thamsborg".) As pessoas que preferem a semântica operacional conhecem esse material como relações lógicas indexadas por etapas. (Por exemplo, consulte "Independência da representação dependente do Estado" de Ahmed, Dreyer e Rossberg.) De qualquer maneira, as técnicas utilizadas são relativamente novas.

a -> bumaTbumabT(UMA)umaa

No que diz respeito à teoria equacional, uma vez que essas línguas podem ser descritas por traduções em subconjuntos ligeiramente diferentes da mesma língua, é inteiramente justo chamá-las de variações sintáticas uma da outra.

A diferença de sensação entre ML e Haskell na verdade decorre das propriedades intensivas das duas linguagens - isto é, tempo de execução e consumo de memória. O ML possui um modelo de desempenho composicional (ou seja, o custo de tempo / espaço de um programa pode ser calculado a partir dos custos de tempo / espaço de seus subtermos), assim como seria uma verdadeira linguagem de chamada por nome. O Haskell real é implementado com chamada por necessidade, um tipo de memorização e, como resultado, seu desempenho não é composicional - o tempo que uma expressão vinculada a uma variável leva para avaliar depende se ela foi usada antes ou não. Isso não é modelado na semântica a que aludi acima.

Se você deseja levar as propriedades intensionais mais a sério, ML e Haskell começam a mostrar diferenças mais sérias. Provavelmente ainda é possível conceber uma metalinguagem comum para eles, mas a interpretação dos tipos diferirá de uma maneira muito mais sistemática, relacionada à idéia de focar na teoria da prova . Um bom lugar para aprender sobre isso é a tese de doutorado de Noam Zeilberger.

Neel Krishnaswami
fonte
10

Meu sentido é que o SPJ estava se referindo a linguagens puramente funcionais - ou seja, linguagens que são referencialmente transparentes. Isso inclui, por exemplo, Haskell, Miranda, Clean, mas não ML. Depois de ter uma linguagem puramente funcional, em geral, você pode fornecer uma semântica denotacional bastante limpa e bem definida. Essa semântica, em geral, parecerá uma para o cálculo lambda, com alguns ajustes aqui e ali. Geralmente, você terá um sistema de tipos que desaconselha algo semelhante a uma variante do Sistema F - talvez mais poderoso em alguns aspectos, mais restrito em outros. É por isso que a extração / compilação de código para Haskell, O'Caml etc. é relativamente direta de assistentes sofisticados de prova de tipo dependente, como a Agda.

Dentro desse quadro, há muito espaço para brincar. Certamente, ainda existe uma diferença entre uma linguagem não estrita e uma estrita. No entanto, na ausência de efeitos colaterais, a única diferença é que uma linguagem não estrita contém mais expressões que não indicam fundo - na medida em que as duas estratégias de avaliação não produzem fundo, eles concordam.

A declaração de Simon também se encaixa em um contexto histórico muito importante. Na época do nascimento de Haskell (1987), havia uma panóplia de linguagens funcionais não estritas - não apenas Miranda, mas Lazy ML, Orwell, Clean e muitas outras. Além de certas variações sintáticas, todas eram praticamente a mesma linguagem. Qual foi precisamente a motivação para o Comitê Haskell se formar. Para obter mais informações, consulte "Uma história de Haskell: ser preguiçoso com a classe": http://research.microsoft.com/en-us/um/people/simonpj/papers/history-of-haskell/ .

sclv
fonte
5

Eu acho que o SPJ está correto ao dizer isso para a semântica central.

Embora existam muitas sutilezas avançadas, como a falta de avaliação estrita ou preguiçosa, como você mencionou, os detalhes dos sistemas de tipos ou a organização de unidades de código maiores (módulos, estruturas), o modelo mental de um programa é muito semelhante entre linguagens funcionais.

Escolha alguma função específica especificada e escreva-a em todos os idiomas que você está comparando, e você provavelmente descobrirá que a estrutura e a semântica das diferentes implementações serão muito semelhantes para todas elas, incluindo nível de abstração, estruturas de dados escolhidas, elas ' todos assumirão a coleta de lixo, etc.

Pelo contrário, digamos que uma implementação C versus uma implementação Smalltalk da mesma função provavelmente tenha uma estrutura diferente (funções e estruturas de dados de baixo nível x objetos), concentre-se em diferentes níveis de detalhes (por exemplo, gerenciamento manual de memória versus coleta de lixo ) e operam em diferentes níveis de abstração.

A visão de mundo do espaço de design da linguagem funcional é simplesmente mais específica e coerente do que a do espaço de "programação imperativa" que agrupa assemblagens, C, Smalltalk, Forth e dezenas de outras linguagens radicalmente diferentes em uma única categoria.

Marc Hamann
fonte
4

Eu acho que a citação de Simon PJ é um pouco fora de questão.

A semelhança entre idiomas é determinada pelo consenso gerado na comunidade de pesquisadores e designers de idiomas. Não há dúvida de que existe um alto grau de consenso na comunidade de programação funcional do que na comunidade imperativa de programação. Mas também é o caso de que as linguagens de programação funcional são projetadas principalmente por pesquisadores e não por profissionais. Portanto, é natural que esse consenso surja.

Quase todas as linguagens funcionais usam gerenciamento de memória coletada por lixo e estruturas de dados recursivas (originadas pelo Lisp), a maioria delas usa tipos de dados "algébricos" e correspondência de padrões (originada pelo Hope), muitos deles usam funções de ordem superior e funções polimórficas ( originada por ML). Além disso, o consenso desaparece. Eles diferem nos sistemas de módulos utilizados, como devem ser tratadas as operações de mudança de estado e outros efeitos computacionais, e na ordem de avaliação (chamada por nome vs chamada por valor) etc.

As linguagens de programação imperativas geralmente usam estruturas de controle aninhadas (originadas pelo Algol 60) e sistemas de tipos (originados pelo Algol 60, mas consolidados pelo Algol 68). Eles geralmente têm uma sintaxe de superfície complicada (voltando novamente ao Algol 60), fazem tentativas sem sentido de lidar com funções de ordem superior e tipos polimórficos e diferem em seu suporte à estrutura de blocos e sistemas de módulos. Provavelmente, há mais uniformidade na ordem de avaliação porque, após os anos 60, a chamada por nome desapareceu essencialmente das linguagens imperativas.

Portanto, não está claro para mim que a diferença entre as duas classes de idiomas em sua uniformidade seja tão grande.

Seria realmente interessante trazer as anotações mais limpas e uniformes da programação funcional para linguagens de programação imperativas. Vejo que Scala começou nessa direção. Resta ver se a tendência continuará.

Uday Reddy
fonte
Eu me pergunto por que você usou as aspas assustadoras em tipos de dados "algébricos"?
Steven Shaw