2012-04-25 15 views
17

Tengo un código que usa métodos múltiples y, idealmente, me gustaría sobrecargar la función (en este caso, multifunción) para que pueda pasar una función de orden superior para ayudar con las pruebas, por ejemplo.¿Es posible sobrecargar los métodos múltiples de Clojure en Arity?

Aquí está el ejemplo:

(ns multi) 

(defn my-print [m] (println "The colour is" (:colour m))) 

(defmulti which-colour-mm (fn [m f] (:colour m))) 

(defmethod which-colour-mm :blue [m f] (f m)) 
(defmethod which-colour-mm :red [m f] (f m)) 
(defmethod which-colour-mm :default [m f] (println "Default: Neither Blue nor Red")) 

(defn which-colour 
    ([m] (which-colour-mm m my-print)) 
    ([m f] (which-colour-mm m f))) 

(which-colour {:colour :blue :object :ball}) 
(which-colour {:colour :yellow :object :ball}) 
(which-colour {:colour :blue :animal :parrot} (fn [m] (println "The " (:animal m) "is" (:colour m)))) 

Así que mi defn proporciona la aridad sobrecarga pero me pregunto si es compatible con defmethod nada como esto. (Supongo que no querría hacerlo para cada declaración de defmethod.)

¿Es este el enfoque más adecuado (me atrevo a decir, idiomático), o hay una manera mejor?

Respuesta

14

Esto está perfectamente bien. Existe la interfaz de "usuario" y la interfaz de "tipo" de una biblioteca. Pueden ser idénticos, pero no es necesario.

La interfaz de "usuario" está en su caja which-colour. La interfaz de "tipo" es which-colour-mm (vale, no realmente, pero solo por el argumento). El usuario de su biblioteca no necesita saber sobre el multimétodo.

Por otro lado, alguien que proporciona un nuevo color, digamos :purple, no tiene que preocuparse por el texto repetido de varias arias. Esto se maneja para él en which-colour.

¡Este es un diseño perfectamente válido!

Pero, por supuesto, hay una etiqueta de precio: supongamos que tiene un color, que tiene una forma más eficaz de hacer las cosas ... Ahora, está bloqueado en una posible interfaz más lenta.

Para aclarar esto un poco: Supongamos que tiene una interfaz de colección. Proporciona una función: conj, que permite al usuario agregar elementos a la colección. Se implementa como esto:

(defn conj 
    [coll & elements] 
    (reduce conj1 coll elements)) 

conj1 es la interfaz "tipo" (por ejemplo una función multimethod o protocolo.): Se añade un elemento a la colección. Entonces, alguien que suministra un nuevo tipo de colección solo tiene que implementar el caso simple de agregar un solo argumento. Y automágicamente, el nuevo tipo también admitirá la adición de múltiples elementos.

Pero ahora supongamos que tiene un tipo de colección, que permite una manera más rápida de agregar varios elementos que simplemente agregar uno después del otro. Esta capacidad no se puede usar ahora.

Por lo tanto, usted hace que la función multimethod/protocol tenga la función conj. Ahora la colección puede usar la forma más rápida. Pero cada implementación debe proporcionar la repetición de múltiples elementos.

Esta es una solución de compromiso y depende de su decisión. No hay forma correcta (tm). Ambos pueden considerarse idiomáticos. (Aunque yo personalmente trataría de ir con el primero tantas veces como sea posible.)

YMMV.

Edit: Un ejemplo de métodos multi arity sin codificación en el valor de envío.

(defmulti which-colour-mm (fn [m & args] (:colour m))) 
(defmethod which-colour-mm :blue 
    ([m] (print m)) 
    ([m f] (f m))) 
+0

me gusta esto y la respuesta de Ankur, pero éste utiliza aridad sobrecarga v s el otro que usa el recuento de argumentos para hacer coincidir el valor de envío. Supongo que tiene sentido utilizar el enfoque defn si desea la misma función predeterminada para cada valor de despacho (y evitar la duplicación) frente a la sobrecarga en el nivel de definición si desea un valor predeterminado diferente por valor de despacho. –

3

Usted puede hacer que el uso de métodos múltiples, como se muestra a continuación ejemplo:

(defmulti which-colour-mm (fn [m & args] [(count args) (:colour m)])) 
(defmethod which-colour-mm [0 :blue] [m] (print m)) 
(defmethod which-colour-mm [1 :blue] [m f] (f m)) 


user=> (which-colour-mm {:colour :blue :object :ball}) 
{:colour :blue, :object :ball}nil 
user=> (which-colour-mm {:colour :blue :object :ball} print) 
{:colour :blue, :object :ball}nil 
2

Básicamente se puede despachar en nada, ni el tipo ni el número de argumentos tiene que ser consistent..like esto:

(defn- map-classes [an-object] 
    (let [cmap 
     {1 :thing 
      2 666 
      3 "yada"} 
    the-class (class an-object)] 
    (get cmap an-object the-class))) 

(defn- mk-class [& args] (map #(map-classes %) args)) 
(defmulti play-thing mk-class) 
(defmethod play-thing [:thing] [v] (= 1 v)) 
(defmethod play-thing [666] [v] (= 2 v)) 
(defmethod play-thing ["yada" String] [v x] (str x v)) 

Las posibilidades son infinitas

Cuestiones relacionadas