Composição da função Haskell (.) E idiomas da aplicação da função ($): uso correto

129

Eu tenho lido Haskell do mundo real e estou chegando ao fim, mas uma questão de estilo me incomoda com os operadores (.)e ($).

Quando você escreve uma função que é uma composição de outras funções, você a escreve como:

f = g . h

Mas quando você aplica algo ao final dessas funções, escrevo-o assim:

k = a $ b $ c $ value

Mas o livro escreveria assim:

k = a . b . c $ value

Agora, para mim, eles parecem funcionalmente equivalentes, fazem exatamente a mesma coisa aos meus olhos. No entanto, quanto mais eu olho, mais vejo pessoas escrevendo suas funções da maneira que o livro faz: componha (.)primeiro e depois apenas no final use ($)para acrescentar um valor para avaliar o lote (ninguém o faz com muitas composições em dólares) .

Existe uma razão para usar a maneira livros muito melhor do que usar todos os ($)símbolos? Ou existe alguma prática recomendada aqui que eu não estou conseguindo? Ou é supérfluo e eu não deveria me preocupar com isso?

Robert Massaioli
fonte
4
Observe que o segundo exemplo pode ser feito comok = a $ b $ c value
Thomas Eding
1
Sim, como Zifre mencionou abaixo, mas decidi deixá-lo lá, pois não prejudica nada e faz seus comentários (e agora os seus) fazerem sentido. Obrigado mesmo assim. +1 :)
Robert Massaioli
2
Outra abordagem comum é a a . b $ c valueque não gosto tanto quanto no terceiro exemplo, mas salva alguns caracteres.
John L

Respostas:

153

Eu acho que posso responder isso por autoridade.

Existe uma razão para usar a maneira livros muito melhor do que usar todos os símbolos ($)?

Não há motivo especial. Bryan e eu preferimos reduzir o ruído da linha. .é mais quieto que $. Como resultado, o livro usa a f . g . h $ xsintaxe.

Don Stewart
fonte
3
Bem, não posso esperar uma resposta melhor do que esta; de um dos autores em si. :) E isso faz sentido, parece muito mais silencioso na página enquanto eu leio. Marcando isso como a resposta, porque responde diretamente à pergunta. Obrigado pela resposta; e, de fato, o livro.
Robert Massaioli
38
Não discordo do autor, mas acho que há outra razão mais proeminente no modelo mental de criar algo em vez de usá- lo. Os usuários de Haskell tendem a querer pensar f.g.hcomo uma nova criação inteligente f(g(h())). Agora eles estão chamando uma nova função, embora anônima, que eles criaram, em vez de apenas encadear um grande dicionário de chamadas de função pré-fabricadas como um usuário PHP.
Evan Carroll
1
Essa é uma afirmação interessante: 'reduza o ruído da linha' e 'mais silencioso que'. Eu nunca pensei sobre linguagens de programação nessa terminologia, mas faz todo o sentido.
Rabarberski 27/05
53

Eles são de fato equivalentes: lembre-se de que o $operador não faz, essencialmente, nada. f $ xavalia como f x. O objetivo $é o seu comportamento de fixação: associatividade correta e precedência mínima. Removendo $e usando parênteses para agrupar em vez de precedência de infixo, os trechos de código têm a seguinte aparência:

k = a (b (c (value)))

e

k = (a . b . c) value

O motivo para preferir a .versão à $versão é o mesmo motivo para preferir a versão entre parênteses acima: apelo estético.

Embora alguns possam se perguntar se o uso de operadores infix em vez de parênteses se baseia em algum desejo subconsciente de evitar qualquer possível semelhança com o Lisp (brincadeira ... acho?).

CA McCann
fonte
38

Eu acrescentaria que f . g $ x, f . gé uma unidade sintática significativa.

Enquanto isso, em f $ g $ x, f $ gnão é uma unidade significativa. Uma cadeia de $é sem dúvida mais imperativa - primeiro obtenha o resultado gde x, depois faça fa ela, depois faça fooa ela, então etc.

Enquanto isso, uma cadeia .é sem dúvida mais declarativa e, em certo sentido, mais próxima de uma visão centrada no fluxo de dados - compõe uma série de funções e, em última análise, aplica-as a alguma coisa.

sclv
fonte
2
Estou aprendendo Haskell (não codifiquei nada significativo), mas gosto de pensar em $ como uma espécie de operador "pipe". É muito parecido com o terminal | pipe (exceto os dados fluem da direita para a esquerda), mas, como processos terminais, cada unidade é explicitamente independente.
LostSalad
1
O $operador parece se comportar de maneira semelhante ao <|operador em F #. Acredito que ambos são definidos da mesma forma - em F # (<|) a b = a be em Haskell a $ b = a b, que são essencialmente equivalentes.
21815 Tom Galvin
@ Quackmatic Bastante certo de que está (<|) b a = a bem F #, já que é usado como [1; 2; 3; 4; 5] <| List.map ((+) 1)se a memória servir.
BalinKingOfMoria Reinstala CMs
@BalinKingOfMoria O <|operador passa o valor para a função, em vez de passar a função para o valor - seu exemplo de código funcionaria com o |>operador.
22616 Tom Galvin
@Quackmatic Oh. Não sabia que havia várias versões (não usei muito o F #).
BalinKingOfMoria Reinstate CMs
18

Para mim, acho que a resposta é (a) a limpeza, como Don disse ; e (b) acho que quando estou editando o código, minha função pode acabar no estilo sem pontos, e tudo o que preciso fazer é excluir o último em $vez de voltar e alterar tudo. Um ponto menor, certamente, mas um detalhe.

Antal Spector-Zabusky
fonte
Sim, esse é um bom motivo para escrevê-lo assim, se você quiser terminar em uma composição de funções, será mais rápido. :) Yay por salvar as teclas digitadas, mas o mais importante é o tempo. +1
Robert Massaioli
13

Há uma discussão interessante sobre essa questão nesse tópico de haskell-café . Aparentemente há um ponto de vista minoria que sustenta que a associatividade direita $é "simplesmente errado plain" , e escolher f . g . h $ xmais f $ g $ h $ xé uma forma de contornando a questão.

Travis Brown
fonte
Ei, você encontrou toda uma discussão sobre o assunto. Isso é incrível, estou lendo agora. 1
Robert Massaioli
2
Argumentos semelhantes aqui: ghc.haskell.org/trac/haskell-prime/wiki/…
enrique
Observe que $!também tem a associatividade certa "completamente errada" - por que o $! operador associativo certo? .
imz - Ivan Zakharyaschev 07/07
3

É apenas uma questão de estilo. No entanto, a maneira como o livro faz isso faz mais sentido para mim. Compõe todas as funções e depois aplica-o ao valor.

Seu método parece estranho, e o último $é desnecessário.

No entanto, isso realmente não importa. Em Haskell, geralmente existem muitas, muitas maneiras corretas de fazer a mesma coisa.

Zifre
fonte
3
Eu não percebi que o último $ era desnecessário, mas deveria. Obrigado e vou deixar lá para que as pessoas saibam o que esse comentário significa.
Robert Massaioli
0

Sei que essa é uma pergunta muito antiga, mas acho que há outra razão para isso que não foi mencionada.

Se você estiver declarando uma nova função sem pontos f . g . h, o valor que você passar será aplicado automaticamente. No entanto, se você escrever f $ g $ h, não funcionará.

Eu acho que a razão pela qual o autor prefere o método de composição é porque leva a uma boa prática de criação de funções.

hackeryarn
fonte