Abreviação de função anônima

85

Há algo que não entendo sobre funções anônimas usando a notação curta # (..)

O seguinte funciona:

REPL>  ((fn [s] s) "Eh")
"Eh"

Mas isso não:

REPL>  (#(%) "Eh")

Isso funciona:

REPL> (#(str %) "Eh")
"Eh"

O que não entendo é por que (# (%) "Eh") não funciona e ao mesmo tempo não preciso usar str em ((fn [s] s) "Eh")

Ambas são funções anônimas e ambas assumem, aqui, um parâmetro. Por que a notação abreviada precisa de uma função enquanto a outra notação não?

Cedric Martin
fonte

Respostas:

126
#(...)

é uma abreviatura para

(fn [arg1 arg2 ...] (...))

(onde o número de argN depende de quantos% N você tem no corpo). Então, quando você escreve:

#(%)

é traduzido para:

(fn [arg1] (arg1))

Observe que isso é diferente de sua primeira função anônima, que é como:

(fn [arg1] arg1)

Sua versão retorna arg1 como um valor, a versão que vem da expansão da abreviação tenta chamá-la como uma função. Você obtém um erro porque uma string não é uma função válida.

Uma vez que a abreviação fornece um conjunto de parênteses ao redor do corpo, ela só pode ser usada para executar uma única chamada de função ou forma especial.

Barmar
fonte
65

Como as outras respostas já muito bem assinalou, o #(%)que você postou na verdade se expande para algo como (fn [arg1] (arg1)), o que não é de todo a mesma (fn [arg1] arg1).

@John Flatness apontou que você pode apenas usar identity, mas se estiver procurando uma maneira de escrever identityusando a #(...)macro de despacho, você pode fazer assim:

#(-> %)

Ao combinar a #(...)macro de despacho com a ->macro de threading, ela é expandida para algo como (fn [arg1] (-> arg1)), que se expande novamente para (fn [arg1] arg1), que é exatamente o que você deseja. Também considero a combinação de macro ->e #(...)útil para escrever funções simples que retornam vetores, por exemplo:

#(-> [%2 %1])
DaoWen
fonte
21

Quando você usa #(...), pode imaginar que está escrevendo (fn [args] (...)), incluindo os parênteses que você começou logo após a libra.

Portanto, seu exemplo não funcional converte para:

((fn [s] (s)) "Eh")

que obviamente não funciona porque você está tentando chamar a string de "Eh". Seu exemplo com strfunciona porque agora sua função é em (str s)vez de (s).(identity s)seria o análogo mais próximo de seu primeiro exemplo, uma vez que não forçará str.

Faz sentido se você pensar a respeito, visto que, exceto esse exemplo totalmente mínimo, toda função anônima vai chamar algo , então seria um pouco tolo exigir outro conjunto aninhado de parênteses para realmente fazer uma chamada.

John Flatness
fonte