2010-08-28 11 views
19

que tienen la opción de implementar directamente un Protocolo en el cuerpo de un defrecord en lugar de utilizar extender protocolo/extensión de tipo¿Cuál es el razonamiento detrás de los registros cerrados en Clojure?

(defprotocol Fly 
    (fly [this])) 

(defrecord Bird [name] 
    Fly 
    (fly [this] (format "%s flies" name))) 

=>(fly (Bird. "crow")) 
"crow flies" 

Si ahora intenta reemplazar el protocolo de la mosca, me sale un error

(extend-type Bird 
    Fly 
    (fly [this] (format "%s flies away (:name this))) 

class user.Bird already directly implements interface user.Fly for protocol:#'user/Fly 

Por otro lado, si en lugar utilizo extender de tipo inicialmente

(defrecord Dragon [color]) 

(extend-type Dragon 
    Fly 
    (fly [this] (format "%s dragon flies" (:color this)))) 

=>(fly (Dragon. "Red")) 
"Red dragon flies" 

que pueda entonces el la función de marcha "override"

(extend-type Dragon 
    Fly 
    (fly [this] (format "%s dragon flies away" (:color this)))) 

=>(fly (Dragon. "Blue")) 
"Blue dragon flies away" 

Mi pregunta es, ¿por qué no permitir la extensión en ambos casos? ¿Es esto una limitación de JVM debido a la relación de Registro < -> Clase o hay un caso de uso para un protocolo no invalidable?

Respuesta

26

En un nivel, es un problema que la JVM no permita que las implementaciones de métodos se intercambien dentro y fuera de clases, ya que implementar un protocolo en línea equivale a hacer que la clase creada por defrecord implemente una interfaz correspondiente al protocolo. Tenga en cuenta que, si bien opta por hacerlo, sacrifica cierta flexibilidad, también compra velocidad, y de hecho, la velocidad es la principal consideración de diseño aquí.

En otro nivel, generalmente es una muy mala idea para las implementaciones de protocolo en tipos a ser provistos por código que no posee ni el tipo ni el protocolo en cuestión. Ver por ejemplo this thread en el grupo Clojure Google para una discusión relacionada (incluyendo una declaración de Rich Hickey); también hay una entrada correspondiente en la Clojure Library Coding Standards:

Protocolos:

  • uno sólo debe extender un protocolo a un tipo si es propietario o bien el tipo o el protocolo.
  • Si uno rompe la regla anterior, se debe estar preparado para retirarse, debe el implementador de cualquiera proporcionar una definición
  • Si un protocolo en sí viene con Clojure, evitar extenderla a tipos que no posee, especialmente por ej. java.lang.String y otras interfaces Java centrales . Tenga la seguridad de que si se extiende un protocolo , lo hará, de lo contrario lobby para ello.
    • El motivo es, según lo declarado por Rich Hickey, [a] a prevenir la "gente se extienden los protocolos de tipos para los que que no tienen sentido, por ejemplo, para los que los autores de protocolos considerados, pero rechazados una implementación debido a un desajuste semántico. ". "No hay extensión estará allí (por diseño), y las personas sin suficientes conocimientos/habilidades podrían llenar el vacío con ideas rotas."

Esto también se ha discutido mucho en la comunidad Haskell en relación con las clases de tipos (Google 'instancias huérfanas', hay algunos buenos puestos en el tema aquí en lo mismo)

. .

ahora claramente una aplicación en línea está siempre provista por el dueño del tipo y por lo tanto no debe sustituirse por código de cliente lo tanto, puedo ver dos casos de uso válidos siguen siendo para el reemplazo método de protocolo:

  1. probando cosas en REPL y aplicando ajustes rápidos;

  2. modificando las implementaciones del método de protocolo en una imagen Clojure en ejecución.

(1) podría ser un problema menor si se utiliza extend & Co. en el tiempo de desarrollo y sólo cambia a inline implementaciones en algún momento a finales de rendimiento de precisión; (2) es algo que uno puede sacrificar si se requiere velocidad máxima.

+0

Excelente respuesta, Michal. Aclara una gran confusión sobre el tema. –

+0

Gracias que tenía mucho sentido, y el hilo vinculado ayudó especialmente. – grinnbearit

+1

@BG: ¡Gracias! Todo es mérito de Clojurebane of Confusion +5 (por ejemplo, el grupo de Clojure) y Haskellplate of Enlightenment +5 (por ejemplo, el café Haskell). ;-) @grinnbearit: ¡Feliz de escuchar eso! –

Cuestiones relacionadas