Sintaxe da função em Clojure

Quando eu estava aprendendo Clojure pela primeira vez, achei a sintaxe abreviada para funções anônimas um pouco confusa. Espero que este pequeno artigo sobre as diferentes maneiras de definir funções ajude alguém. Se você está apenas procurando uma atualização sobre a abreviação da função anônima, vá direto para o final.

Se você passou algum tempo com o Clojure, é provável que esteja familiarizado com a definição de função padrão:

(defn my-func [arg1 arg2] (... function body here ...))

Que é uma abreviatura para

(def my-func (fn [arg1 arg2] (... function body here ...)))

Você pode facilmente fazer uma função não pública usando defn-

(defn- my-func [arg1 arg2] (... function body here ...))

Existem várias circunstâncias em que talvez você não precise definir uma função; você pode querer usá-lo apenas uma vez, para um propósito específico, e depois jogá-lo fora. É aí que entram as funções anônimas. Você pode estar familiarizado com elas em Javascript e Ruby, onde funções anônimas são frequentemente usadas como retornos de chamada.

Você já viu a definição básica de função anônima no segundo exemplo acima.

(fn [arg1 arg2] (... function body here ...))

Esta função é anônima porque não está vinculada a um nome específico usando def. Esta definição sozinha não é muito útil: ela precisa de contexto.

Um exemplo simples, concatenando os dois argumentos em uma única string. Você poderia fazer algo assim:

((fn [arg1 arg2] (str arg1 arg2)) "A" "B")

Por causa dos parênteses mais externos, a função anônima é executada imediatamente e passada os dois argumentos “A” e “B”, retornando em última análise “AB”

Este ainda não é um exemplo muito prático. Em Clojure, você frequentemente verá funções anônimas passadas como argumentos para outras funções. No caso da mapfunção, ele aceita uma função como seu primeiro argumento e uma coleção como seu segundo. Esse argumento de função não precisa ser uma função definida por linguagem ou uma função definida pelo usuário, ele pode ser anônimo. Você poderia fazer algo assim:

(map (fn [arg1] (str "Hello: " arg1)) ["A" "B"])

A mapfunção aplica a função anônima a cada elemento da coleção e retorna["A" "B"]("Hello: A" "Hello: B")

Essa é uma função anônima relativamente simples, mas você provavelmente pode ver que já está ficando um pouco difícil de ler. Insira a sintaxe abreviada para funções anônimas. Vamos reproduzir a última função anônima usando a sintaxe abreviada.

#(str "Hello: " %)

É muito menos prolixo do que o original, mas também não é totalmente intuitivo. Vamos decompô-lo. A #()sintaxe é o que diz a Clojure que esta é uma função. É muito equivalente a .(fn [] ())

O %símbolo é usado como uma abreviação para fazer referência aos argumentos passados ​​para a função, para que você não confunda a definição da função nomeando-os explicitamente. Ambos %e farão referência ao primeiro argumento passado, farão referência ao segundo argumento e assim por diante.%1%2

Nossa taquigrafia #(str "Hello: " %)é a mesma que(fn [arg1] (str "Hello: " arg1))

Clojure prestará atenção a quantos argumentos sua função anônima abreviada está esperando e reclamará se receber o número errado de argumentos. Por exemplo:

(#(str "Hello:" % " and " %2) "A")

Esta função espera 2 argumentos, mas recebe apenas um, fazendo com que Clojure diga:

ArityException Wrong number of args (1) passed to: user$eval740$fn  clojure.lang.AFn.throwArity (AFn.java:437)