Por que algumas linguagens de programação funcionais usam um espaço para a aplicação de funções?

34

Tendo examinado algumas linguagens para programação funcional, sempre me perguntei por que algumas linguagens fp usam um ou mais caracteres de espaço em branco para aplicação de funções (e definição), enquanto a maioria (todas?) De linguagens imperativas / orientadas a objetos usa parênteses, o que parece ser a maneira mais matemática. Eu também acho que o último estilo é muito mais claro e legível do que sem os parênteses.

Portanto, se tivermos uma função f (x) = x², existem duas alternativas para chamá-la:

  • FP: f x

    Exemplos:

    • ML, Ocaml, F #
    • Haskell
    • LISP, esquema (de alguma forma)
  • Não FP: f(x)

    Exemplos:

    • Quase todas as línguas imperativas (eu sei, veja os comentários / respostas)
    • Erlang
    • Scala (também permite "notação do operador" para argumentos únicos)

Quais são as razões para "deixar de fora" os parênteses?

pvorb
fonte
9
Para torná-lo mais confuso, erm, eu quis dizer sucinto .
Den
11
@Den se você aprender Haskell, a sintaxe será uma das coisas menos confusas: p
Simon Bergot
5
Você percebe a ironia de dizer que usar parênteses seria mais matemático e depois usar a função de exponenciação como exemplo?
Jörg W Mittag
12
@Den você está se prejudicando por rejeitar um idioma por causa de sua sintaxe. Certamente você não teve problemas para aprender xml ou sql (essas não são linguagens de uso geral, mas elas definem sua própria sintaxe).
Simon Bergot 08/07
7
Eu gostaria de salientar que scripts de shell (por exemplo, bash) geralmente não usam parâmetros para chamar comandos. O PowerShell tem o pior dos dois mundos, pois as funções são declaradas entre parênteses e chamadas sem eles.
Kris Harper

Respostas:

59

que parece ser a maneira mais matemática

linguagens funcionais são inspiradas no cálculo lambda . Nesse campo, parênteses não são usados ​​para aplicação de função.

Eu também acho que o último estilo é muito mais claro e legível do que sem os parênteses.

A legibilidade está nos olhos de quem vê. Você não está acostumado a lê-lo. É um pouco como operadores matemáticos. Se você entende a associatividade, precisa apenas de alguns parênteses para esclarecer a estrutura de sua expressão. Muitas vezes você não precisa deles.

O curry também é um bom motivo para usar esta convenção. No haskell, você pode definir o seguinte:

add :: Int -> Int -> Int
add x y = x + y

x = add 5 6 -- x == 11
f = add 5
y = f 6 -- y == 11
z = ((add 5) 6) -- explicit parentheses; z == 11

Com parens, você pode usar duas convenções: f(5, 6)(sem curry) ou f(5)(6)(curry). A sintaxe haskell ajuda a se acostumar com o conceito de currying. Você ainda pode usar uma versão sem curry, mas é mais doloroso usá-la com combinadores

add' :: (Int, Int) -> Int
add' (x, y) = x + y
u = add'(5, 6) -- just like other languages
l = [1, 2, 3]
l1 = map (add 5) l -- [6, 7, 8]
l2 = map (\x -> add'(5, x)) l -- like other languages

Observe como a segunda versão obriga a registrar x como uma variável e que a subexpressão é uma função que pega um número inteiro e adiciona 5 a ele? A versão ao curry é muito mais leve, mas também é considerada por muitos como mais legível.

O programa Haskell faz uso extensivo de aplicativos e combinadores parciais como um meio de definir e compor abstrações, portanto esse não é um exemplo de brinquedo. Uma boa interface de funções será aquela em que a ordem dos parâmetros forneça um uso amigável de curry.

Outro ponto: uma função sem parâmetros deve ser chamada com f(). Em haskell, como você manipula apenas valores imutáveis ​​de avaliação preguiçosa, basta escrever fe considerá-lo como um valor que precisará executar alguns cálculos quando necessário. Como sua avaliação não terá efeito colateral, não faz sentido ter uma notação diferente para a função sem parâmetros e seu valor retornado.

Existem também outras convenções para aplicação de funções:

  • Lisp: (fx) - prefixo com parênteses externos
  • Quarto: xf - postfix
Simon Bergot
fonte
7
Nitpick menor: os termos são "currying" e "curried", não "currification" e "currified".
Doval
"non curried" o que é uma função não curried no haskell?
Ven
3
@ user1737909 Uma função que utiliza uma tupla como argumento.
Doval
11
@Doval Na verdade, deve ser "schönfinkeling" e "schönfinkeled". Mas tudo bem, a história sempre define o vencedor retrospectivamente.
Profpatsch
Pensando em uma sintaxe de curry entre parênteses, algo como f(a, ,b)ou add(a, )ou add(, b)pareceria mais flexível por padrão.
Phresnel #
25

A idéia básica é tornar a operação mais importante (aplicativo de funções) mais fácil de ler e mais fácil de escrever. Um espaço é muito pouco intrusivo para ler e muito fácil de digitar.

Observe que isso não é específico para linguagens funcionais, por exemplo, no Smalltalk , um dos primeiros idiomas OO e idiomas inspirados por ele ( Self , Newspeak , Objective-C), mas também em Io e idiomas inspirados por ele (Ioke, Seph), espaço em branco é usado para chamadas de método.

Estilo Smalltalk:

anArray sort
anArray add: 2
aString replace: "a" with: "b"

(No último caso, o nome do método é replace:with:.)

Estilo Io:

anArray sort
anArray add(2)
aString replace("a", "b")

Scala também permite espaço em branco para chamadas de método:

foo bar(baz)

E deixando de fora os parênteses se houver apenas um argumento:

foo bar baz

O Ruby também permite que você pare os parênteses:

an_array.sort
an_array.add 2
a_string.replace "a", "b"

usando parênteses, que parece ser a maneira mais matemática

Na verdade não:

f(x)
sin x
x²
|x|
x!
x + y
xy
½

A notação matemática evoluiu por um longo tempo de uma maneira terrivelmente inconsistente.

Se houver, as linguagens funcionais se inspiram no cálculo λ, onde a aplicação da função é escrita usando espaço em branco.

Jörg W Mittag
fonte
3
Há também um argumento para a economia da edição. Em particular, linguagens funcionais geralmente oferecem suporte direto à composição. Colocar parênteses em torno da função em vez do argumento faz sentido. Você só precisa fazer isso "uma vez": (por exemplo, g) x em oposição a e (f (g (x))). Além disso, Haskell, pelo menos, não impediria que alguém fizesse f (x) em vez de f x. Não é apenas idiomático.
nomen
18

O parêntese para a aplicação de funções é apenas uma das muitas selas que Euler nos deixou. Como qualquer outra coisa, a matemática precisa de convenções quando existem várias maneiras de fazer alguma coisa. Se a sua educação matemática se estende apenas a uma disciplina não matemática na universidade, provavelmente você não está familiarizado com os muitos campos em que a aplicação de funções acontece felizmente sem nenhum desses colchetes sem sentido (por exemplo, Geometria Diferencial, Álgebra Abstrata). E você nem menciona funções aplicadas infixo (quase todas as linguagens), "outfix", como adotar uma norma ou diagramaticamente (Befunge, Algebraic Topology).

Então, em resposta, eu diria que é devido a uma proporção muito maior de programadores funcionais e designers de linguagem que possuem extensa educação matemática. Von Neumann diz: "Jovem, em matemática, você não entende as coisas. Você apenas se acostuma com elas.", Certamente isso é verdade para a notação.

Sean D
fonte
7
Nem me fale sobre f(x)vs. sin xvs. vs. |x|vs. x!vs. x + yvs. xyvs. ½vs. um somatório vs. uma vs. integrante ...
Jörg W Mittag
2
+1 para a citação de von Neuman. +1 no restante da resposta, mas não posso dar +2. :(
pvorb
2
Eu pego uma pitada de elitismo aqui; Eu discuto a neutralidade de "designers de linguagens de programação funcionais são mais instruídos" .
CaptainCodeman
7
@CaptainCodeman Ele diz que é mais educado em matemática, uma afirmação com a qual concordo. Pelo menos no que diz respeito a Haskell, você pode notar que ambos os conceitos são de natureza mais matemática e a comunidade também é mais inclinada matematicamente. Eles não são mais inteligentes, apenas educados em matemática.
Paulo
5
@CaptainCodeman Não, como ele nunca deu a entender que eles são mais instruídos em geral, apenas mais instruídos em matemática. Foi exatamente o que ele disse: "extensa educação matemática". :)
Paul
8

Embora exista muita verdade na resposta de Simon, acho que também há uma razão muito mais prática. A natureza da programação funcional tende a gerar muito mais parênteses do que a programação imperativa, devido ao encadeamento e composição de funções. Esses padrões de encadeamento e composição também costumam ser capazes de ser inequivocamente representados sem parênteses.

A linha inferior é, todos esses parênteses ficam irritantes para ler e acompanhar. É provavelmente o principal motivo pelo qual o LISP não é mais popular. Portanto, se você pode obter o poder da programação funcional sem o incômodo de excesso de parênteses, acho que os designers de linguagem tendem a seguir esse caminho. Afinal, os designers de idiomas também são usuários de idiomas.

Até o Scala permite que o programador omita parênteses em determinadas circunstâncias, que aparecem com bastante frequência ao programar em um estilo funcional, para que você obtenha o melhor dos dois mundos. Por exemplo:

val message = line split "," map (_.toByte)

As parênteses no final são necessárias para associatividade, e as demais são deixadas de fora (assim como os pontos). Escrever dessa maneira enfatiza a natureza encadeada das operações que você está executando. Pode não ser familiar para um programador imperativo, mas para um programador funcional parece muito natural e fluente escrever dessa maneira, sem ter que parar e inserir sintaxe que não agrega significado ao programa, mas existe apenas para deixar o compilador feliz .

Karl Bielefeldt
fonte
2
"O ponto principal é que todos esses parênteses são chatos de ler e rastrear. É provavelmente o motivo número um do LISP não ser mais popular.": Eu não acho que parênteses sejam uma razão para o Lisp não ser popular: eu não acho eles são particularmente difíceis de ler e acho que outros idiomas têm uma sintaxe muito mais estranha.
Giorgio
2
A sintaxe do LISP compensa amplamente os parênteses irritantes com seu alto grau de ortogonalidade. Isso apenas significa que ele tem outros recursos redentores que o tornam ainda utilizável, não que os parênteses não sejam irritantes. Entendo que nem todos se sentem assim, mas a grande maioria dos programadores que conheci o faz.
Karl Bielefeldt
"Lost In Stupid Parentheses" é o meu backronym favorito do LISP. Os computadores também podem usar a maneira humana de especificar blocos de código e eliminar a redundância, como em idiomas como Wisp .
precisa saber é o seguinte
4

Ambas as premissas estão erradas.

  • Essas linguagens funcionais não usam espaço para aplicação de funções. O que eles fazem é simplesmente analisar qualquer expressão que vem depois de uma função como argumento.

    GHCi> f 3
    4
    GHCi> f (3)
    4
    GHCi> (f) 3
    4

    é claro que se você não usar um espaço nem parênteses, normalmente não funcionará, simplesmente porque a expressão não está adequadamente tokenizada

    GHCi> f3

    <interativo>: 7: 1:
        Não está no escopo: 'f3'
        Talvez você quis dizer 'f' (linha 2)

    Mas se esses idiomas fossem restritos a nomes de variáveis ​​de 1 caractere, também funcionaria.

  • As convenções de matemática também normalmente não exigem parênteses para a aplicação da função. Os matemáticos não apenas escrevem frequentemente sin x - para funções lineares (normalmente chamadas de operadores lineares) também é muito comum escrever o argumento sem parênteses, talvez porque (assim como qualquer função na programação funcional) as funções lineares sejam frequentemente tratadas sem fornecer diretamente um argumento.

Realmente, sua pergunta se resume a: por que alguns idiomas exigem parênteses para a aplicação de funções? Bem, aparentemente algumas pessoas, como você, consideram isso mais legível. Eu discordo totalmente disso: um par de parênteses é bom, mas assim que você tem mais dois aninhados, começa a ficar confuso. O código Lisp demonstra isso melhor e praticamente todas as linguagens funcionais pareceriam desordenadas se você precisasse de parênteses em todos os lugares. Isso não acontece tanto em linguagens imperativas, mas principalmente porque essas linguagens não são expressivas o suficiente para escrever uma linha concisa e clara em primeiro lugar.

leftaroundabout
fonte
Entendi, a pergunta não é perfeita. :) Portanto, a conclusão a ser tirada é: "Parênteses não escalam".
Pvorb
2
Nem são línguas não-funcionais que unânimes em exigir parênteses; por alguns exemplos, Smalltalk object method: args, Objective C [object method: args], e Ruby e Perl permitem omitir parênteses nas chamadas de função e método, exigindo apenas que resolvam a ambiguidade.
Hbbs
1

Um pequeno esclarecimento histórico: a notação original em matemática estava sem parênteses !

A notação fx para uma função arbitrária de x foi introduzida por Johan Bernoulli por volta de 1700, logo após Leibniz começar a falar sobre funções (na verdade, Bernoulli escreveu x ). Porém, antes disso, muitas pessoas já usavam notações como sin x ou lx (o logaritmo de x ) para funções específicas de x , sem parênteses. Eu suspeito que essa seja a razão pela qual muitas pessoas hoje ainda escrevem pecado x em vez de pecado (x) .

Euler, que era aluno de Bernoulli, adotou a orientação de Bernoulli. x de e transformou o grego em uma letra latina, escrevendo fx . Mais tarde, ele percebeu que seria fácil confundir f para uma "quantidade" e acreditava que fx era um produto, então ele começou a escrever f: x . Portanto, a notação f (x) não se deve a Euler. É claro que Euler precisava de parênteses pela mesma razão, ainda precisamos de parênteses em linguagens de programação funcional: para distinguir, por exemplo (fx) + y de f (x + y) . Eu suspeito que as pessoas depois de Euler adotaram o parêntese como padrão porque viram principalmente Euler escrever expressões compostas como f (ax + b). Ele raramente escrevia apenas fx .

Para mais informações, veja: Euler já escreveu f (x) entre parênteses? .

Não sei se os designers de linguagens de programação funcional ou os inventores do cálculo lambda estavam cientes das raízes históricas de sua notação. Pessoalmente, acho a notação sem parênteses mais natural.

Michael Bächtold
fonte