2011-09-16 31 views
6

Parte de lo que es tan poderoso acerca de Clojure es que todos los tipos de datos centrales implementan la misma abstracción de secuencia: clojure.lang.ISeq.Agregar comportamiento personalizado a las secuencias de Clojure

Esto significa que funciones como "primero", "concat", "contras", "map", "rest", etc. funcionan genéricamente en todos esos tipos de datos.

Mi pregunta es esta: ¿cómo puedo agregar mi propia función personalizada en la mezcla y hacer que funcione para todos los tipos que se extienden desde ISeq?

Un primer intento fue definir mi propio protocolo, luego "(extender tipo clojure.lang.ISeq ...", pero eso no funciona (compila pero no agrega el comportamiento a los tipos reales) Otra idea era escribir una macro que hiciera un "extender-tipo" explícitamente en todos los tipos Clojure (PersistentHashMap, PersistentList, etc.), pero eso parece kludgey.

¿Hay alguna manera elegante/idiomática de hacer esto? ? métodos múltiples, tal vez?

Respuesta

8

¿Qué estás tratando de hacer exactamente?

Si está intentando agregar un comportamiento a los tipos existentes: escriba las funciones normales que se ocupan de los seqs o utilice los métodos multimétodos o extend para hacer lo que quiera.

Además, algo a tener en cuenta es que la mayoría de los tipos de "secuencias" de Clojure (vectores, conjuntos, mapas) no son en sí mismas secuencias (no implementan clojure.lang.ISeq), por lo que debe hacer algo más que agregar clojure.lang.ISeq deseo apoyarlos

+5

No sé por qué esto tiene downvoted. Esta es la respuesta correcta.Si desea escribir una función que actúe sobre seqs, solo escriba una función que actúe sobre las seqs, y llame (seq arg) en esa función para convertir cualquier colección sequible pasada a una seq. –

3

Hay un artículo de IBM developerWorks por Stuart Sierra titulado Solving the Expression Problem with Clojure 1.2 que podrían proporcionar información y una respuesta a su pregunta.

Utiliza protocolos para definir un grupo de funciones en varios tipos de datos, y usa extend para extender las clases existentes (tipos de datos) para que puedan usar estas funciones. Puede que no sea exactamente lo que quieres, pero podría ser una forma de resolver tu problema.

También muestra cómo se pueden definir tipos de datos personalizados (usando defrecord y deftype) que implementan protocolos/interfaces existentes.

3

La mejor manera de hacerlo es escribiendo sus nuevas funciones usando funciones Clojure genéricas existentes que manejan correctamente los diferentes tipos de datos.

Ejemplos de tales funciones genéricas:

  • into agrega elementos a una colección de cualquier tipo
  • empty devuelve una colección vacía del mismo tipo, ya que es el parámetro

Entonces se puede escribir su propia función genérica que explota estos, por ejemplo:

(defn take-every-other [coll] 
    (into 
    (empty coll) 
    (map first (partition 2 coll)))) 

(take-every-other [1 2 3 4 5 6]) 
=> [1 3 5] 

(take-every-other {:a 1 :b 2 :c 3 :d 4}) 
=> {:a 1, :c 3} 

Si aún necesita una función más genérica, siempre puede sumergirse en la fuente Clojure para ver cómo se escriben estas funciones.

Cuestiones relacionadas