2012-05-21 619 views
8

A veces me ha parecido conveniente en Clojure definir versiones de funciones de aridad reducida, que devuelven una función parcial, p.Definición de funciones parciales de aria reducida

(defn prefix 
    ([pre string] 
    (str pre ":" string)) 

    ([pre] 
    (fn [string] 
     (prefix pre string)))) 

Esto significa que se puede hacer ya sea:

(prefix "foo" 78979) 
=> "foo:78979" 

((prefix "foo") 78979) 
=> "foo:78979" 

Esto parece bastante Haskell-ish y evita la necesidad de partial para crear funciones parciales.

¿Pero se considera que es un buen diseño de API/estilo de codificación en Lisp?

+0

Esto es currying, ¿sí? Parece útil. Y creo que la interfaz aquí podría mejorarse. Sería bueno si pudieras hacer esta técnica genérica; es decir, para funciones con todas las palabras clave, si llama a esa función con un conjunto incompleto de palabras clave, devuelve un cierre que acepta todas las palabras clave restantes. Si se consumen todas las palabras clave, obtendrá el valor de la función. Pude ver que esto se usa como una técnica de optimización. –

+0

Creo que sigue siendo ideomático si nombras parciales para legibilidad cuando los defines así: (def prefix (partial ...)) Así que tiene los dos: Un nombre para referirse y es explícito.Tenga en cuenta que no utiliza defn ahora pero def-partial está creando la función para usted – KIMA

Respuesta

7

En los idiomas en los que las funciones se curry de manera predeterminada, las llamadas a funciones también se curry. Cuando uno escribe (f a b c), el lenguaje lo interpreta como (((f a) b) c). Este no es el caso en Clojure.

creo que haciendo las funciones de curry en un entorno donde las llamadas no están al curry por defecto crea un desajuste conceptual - (. Me refiero a los lectores humanos de su código) esta construcción probablemente provocará confusión en los lectores

Si su función tiene más de 2 argumentos, la definición se pone fea rápidamente. Supongamos que una función tiene 4 argumentos. Para emular completamente la llamada de currying, debe manejar casos como ((f a b) c d) cuando alguien primero pasa 2 argumentos y luego los dos restantes. En este caso, la versión sobrecargada de la función de dos argumentos necesita devolver una función sobrecargada que se comporta de manera diferente dependiendo de si obtiene 1 o 2 argumentos. Supongo que es posible automatizar eso con macros, pero aún así.

Además, se elimina la posibilidad de definir argumentos predeterminados y la construcción & rest.

+0

argumentos predeterminados y funciones 'y resto' variables – 6502

+0

@ 6502 Sí. Editado para completar. –

6

hmmm ... personalmente, prefiero ver partial, ya que deja en claro lo que está sucediendo.

No sé si es el estilo de codificación "bueno" o "malo", pero nunca he visto este estilo en el código existente de Clojure y me imagino que las personas que usan una API esperarían tanto (prefix "foo" 78979) como (prefix "foo") devuelve el mismo tipo de objeto.

Para hacer la diferencia entre las dos funciones claras que podría hacer algo como esto en su lugar:

(defn prefix [pre string] 
    (str pre ":" string)) 

(defn prefix-fn [pre] 
    (fn [string] 
    (prefix pre string))) 

(prefix "foo" 78979)  ; => "foo:78979" 
((prefix-fn "foo") 78979) ; => "foo:78979" 
+2

"Nunca he visto este estilo antes en el código Clojure existente" --- está en la nueva biblioteca de reductores de Hickey. –

+0

tiene usted razón, gracias por señalarlo. – Gert

9

Usando partial para crear una función de curry se basa en el concepto de Explícito es mejor (en la mayoría de los casos:)). Y he encontrado que este concepto para ser más aplicable/usado en lenguajes de tipado dinámico como Clojure, Python, etc. puede ser debido a la firma de tipo faltante/tipado estático que tiene más sentido hacer las cosas explícitas.

Cuestiones relacionadas