Al compilar mi aplicación Haskell con la -Wall
opción, GHC se queja de instancias huérfanas, por ejemplo:
Publisher.hs:45:9:
Warning: orphan instance: instance ToSElem Result
La clase de tipo ToSElem
no es mía, está definida por HStringTemplate .
Ahora sé cómo solucionar esto (mueva la declaración de la instancia al módulo donde se declara Result), y sé por qué GHC preferiría evitar las instancias huérfanas , pero sigo creyendo que mi camino es mejor. No me importa si el compilador tiene inconvenientes, más que yo.
La razón por la que quiero declarar mis ToSElem
instancias en el módulo Publisher es porque es el módulo Publisher el que depende de HStringTemplate, no de los otros módulos. Estoy tratando de mantener una separación de preocupaciones y evitar que cada módulo dependa de HStringTemplate.
Pensé que una de las ventajas de las clases de tipos de Haskell, en comparación, por ejemplo, con las interfaces de Java, es que son abiertas en lugar de cerradas y, por lo tanto, las instancias no tienen que declararse en el mismo lugar que el tipo de datos. El consejo de GHC parece ser ignorar esto.
Entonces, lo que estoy buscando es alguna validación de que mi pensamiento es sólido y que estaría justificado ignorar / suprimir esta advertencia, o un argumento más convincente en contra de hacer las cosas a mi manera.
Respuestas:
Entiendo por qué quiere hacer esto, pero desafortunadamente, puede ser solo una ilusión que las clases de Haskell parecen estar "abiertas" en la forma en que usted dice. Mucha gente siente que la posibilidad de hacer esto es un error en la especificación de Haskell, por las razones que explicaré a continuación. De todos modos, si realmente no es apropiado para la instancia, debe declararse en el módulo donde se declara la clase o en el módulo donde se declara el tipo, eso probablemente sea una señal de que debería estar usando una
newtype
o alguna otra envoltura alrededor de su tipo.Las razones por las que deben evitarse las instancias huérfanas son mucho más profundas que la conveniencia del compilador. Este tema es bastante controvertido, como puede ver en otras respuestas. Para equilibrar la discusión, voy a explicar el punto de vista de que uno nunca, nunca, debe escribir instancias huérfanas, que creo que es la opinión mayoritaria entre los Haskellers experimentados. Mi propia opinión está en algún punto intermedio, que explicaré al final.
El problema surge del hecho de que cuando existe más de una declaración de instancia para la misma clase y tipo, no hay ningún mecanismo en Haskell estándar para especificar cuál usar. Más bien, el programa es rechazado por el compilador.
El efecto más simple de eso es que podría tener un programa que funcione perfectamente y que de repente deje de compilarse debido a un cambio que alguien más hace en alguna dependencia lejana de su módulo.
Peor aún, es posible que un programa en funcionamiento comience a fallar en tiempo de ejecución debido a un cambio distante. Podría estar usando un método que asume que proviene de una determinada declaración de instancia, y podría ser reemplazado silenciosamente por una instancia diferente que sea lo suficientemente diferente como para hacer que su programa comience a fallar inexplicablemente.
Las personas que quieren garantías de que estos problemas nunca les sucederán deben seguir la regla de que si alguien, en cualquier lugar, alguna vez ha declarado una instancia de cierta clase para un cierto tipo, ninguna otra instancia debe volver a declararse en ningún programa escrito. Por cualquiera. Por supuesto, existe la solución de usar a
newtype
para declarar una nueva instancia, pero eso siempre es al menos un inconveniente menor y, a veces, uno importante. Entonces, en este sentido, aquellos que escriben instancias huérfanas intencionalmente están siendo bastante descorteses.Entonces, ¿qué se debe hacer con este problema? El campo anti-instancia huérfana dice que la advertencia de GHC es un error, debe ser un error que rechace cualquier intento de declarar una instancia huérfana. Mientras tanto, debemos ejercitar la autodisciplina y evitarlos a toda costa.
Como has visto, hay quienes no están tan preocupados por esos posibles problemas. De hecho, fomentan el uso de instancias huérfanas como una herramienta para la separación de preocupaciones, como usted sugiere, y dicen que uno debe asegurarse, caso por caso, de que no haya ningún problema. Las instancias huérfanas de otras personas me han molestado suficientes veces para convencerme de que esta actitud es demasiado arrogante.
Creo que la solución correcta sería agregar una extensión al mecanismo de importación de Haskell que controlaría la importación de instancias. Eso no resolvería los problemas por completo, pero ayudaría en cierta medida a proteger nuestros programas contra el daño de las instancias huérfanas que ya existen en el mundo. Y luego, con el tiempo, podría convencerme de que, en ciertos casos limitados, quizás una instancia huérfana no sea tan mala. (Y esa misma tentación es la razón por la que algunos en el campo anti-instancia huérfana se oponen a mi propuesta).
Mi conclusión de todo esto es que, al menos por el momento, le recomendaría encarecidamente que evite declarar cualquier instancia huérfana, para ser considerado con los demás si no es por otra razón. Utilice un
newtype
.fuente
¡Adelante, suprime esta advertencia!
Estás en buena compañía. Conal lo hace en "TypeCompose". "chp-mtl" y "chp-transformers" lo hacen, "control-monad-exception-mtl" y "control-monad-exception-monadsfd" lo hacen, etc.
por cierto, probablemente ya lo sepas, pero para aquellos que no lo hacen y tropiezan con tu pregunta en una búsqueda:
{-# OPTIONS_GHC -fno-warn-orphans #-}
Editar:
Reconozco los problemas que Yitz mencionó en su respuesta como problemas reales. Sin embargo, no veo el uso de instancias huérfanas como un problema también, y trato de elegir el "menor de todos los males", que es en mi humilde opinión utilizar prudentemente instancias huérfanas.
Solo utilicé un signo de exclamación en mi respuesta corta porque su pregunta muestra que ya conoce bien los problemas. De lo contrario, habría sido menos entusiasta :)
Un poco de diversión, pero lo que creo que es la solución perfecta en un mundo perfecto sin compromiso:
Creo que los problemas que menciona Yitz (sin saber qué instancia se elige) podrían resolverse en un sistema de programación "holístico" donde:
De vuelta del mundo de fantasía (o, con suerte, del futuro), ahora mismo: recomiendo tratar de evitar las instancias huérfanas mientras las sigues usando cuando "realmente necesitas"
fuente
Las instancias huérfanas son una molestia, pero en mi opinión, a veces son necesarias. A menudo combino bibliotecas donde un tipo proviene de una biblioteca y una clase proviene de otra biblioteca. Por supuesto, no se puede esperar que los autores de estas bibliotecas proporcionen instancias para cada combinación concebible de tipos y clases. Así que tengo que proporcionarlos y son huérfanos.
La idea de que debe envolver el tipo en un nuevo tipo cuando necesita proporcionar una instancia es una idea con mérito teórico, pero es demasiado tediosa en muchas circunstancias; es el tipo de idea que presentan las personas que no se ganan la vida escribiendo código Haskell. :)
Así que adelante, proporcione instancias huérfanas. Son inofensivos.
Si puede bloquear ghc con instancias huérfanas, entonces eso es un error y debe informarse como tal. (El error que ghc tenía / tiene acerca de no detectar múltiples instancias no es tan difícil de solucionar).
Pero tenga en cuenta que en algún momento en el futuro alguien más podría agregar la instancia some como ya lo ha hecho, y podría obtener un error (tiempo de compilación).
fuente
(Ord k, Arbitrary k, Arbitrary v) ⇒ Arbitrary (Map k v)
uso de QuickCheck.En este caso, creo que el uso de instancias huérfanas está bien. La regla general para mí es: puede definir una instancia si "posee" la clase de tipos o si "posee" el tipo de datos (o algún componente del mismo, es decir, una instancia para Maybe MyData también está bien, al menos a veces). Dentro de esas restricciones, el lugar donde decida colocar la instancia es asunto suyo.
Hay una excepción más: si no posee la clase de tipos ni el tipo de datos, pero está produciendo un binario y no una biblioteca, también está bien.
fuente
(Sé que llego tarde a la fiesta, pero esto puede ser útil para otros)
Puede mantener las instancias huérfanas en su propio módulo, entonces si alguien importa ese módulo es específicamente porque lo necesita y puede evitar importarlo si causa problemas.
fuente
En este sentido, entiendo la posición de las bibliotecas WRT del campo de instancias anti-huérfanas, pero para los objetivos ejecutables, ¿no deberían estar bien las instancias huérfanas?
fuente