haskell ghc typeclass

Instancias huérfanas en Haskell



ghc typeclass (6)

¡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 sepan esto, pero para aquellos que no lo hacen y tropiezan con su 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, y ​​trato de elegir el "menor de todos los males", que es prudente usar instancias huérfanas.

Solo utilicé un signo de exclamación en mi respuesta corta porque tu pregunta muestra que ya eres muy consciente de 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 es elegida) podrían resolverse en un sistema de programación "holístico" donde:

  • No está editando simples archivos de texto primitivamente, sino que es más bien asistido por el entorno (por ejemplo, la finalización del código solo sugiere cosas de tipos relevantes, etc.)
  • El lenguaje de "nivel inferior" no tiene soporte especial para las clases de tipo, y en su lugar las tablas de funciones se pasan explícitamente
  • Sin embargo, el entorno de programación de "nivel superior" muestra el código de forma similar a cómo se presenta Haskell ahora (por lo general, no se verán las tablas de funciones), y selecciona las clases de tipos explícitas cuando son obvias (para Por ejemplo, todos los casos de Functor tienen una sola opción) y cuando hay varios ejemplos (lista de compresión Applicative o list-monad Applicative, First / Last / lift maybe Monoid) le permite elegir qué instancia usar.
  • En cualquier caso, incluso cuando la instancia se seleccionó para usted automáticamente, el entorno le permite ver fácilmente qué instancia se utilizó, con una interfaz fácil (un hipervínculo o una interfaz de desplazamiento o algo así)

De vuelta del mundo de fantasía (o con suerte del futuro), ahora mismo: recomiendo evitar las instancias huérfanas mientras las sigues usando cuando realmente "necesitas"

Al compilar mi aplicación Haskell con la opción -Wall , GHC se queja de las 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 (mover la declaración de la instancia al módulo donde se declara el resultado), 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 bien que yo.

La razón por la que deseo declarar mis instancias de ToSElem en el módulo de Publicador es porque es el módulo de Publicador que depende de HStringTemplate, no 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, cuando se compara, 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 confirmación de que mi pensamiento es sólido y que estaría justificado al ignorar / suprimir esta advertencia, o un argumento más convincente en contra de hacer las cosas a mi manera.


(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, luego, si alguien importa ese módulo, es específicamente porque lo necesita y puede evitar importarlo si causa problemas.


En esta línea, entiendo las bibliotecas WRT de posición del campo de instancia antihuérfano, pero para los objetivos ejecutables ¿no deberían las instancias huérfanas estar bien?


En este caso, creo que el uso de instancias huérfanas está bien. La regla general para mí es que puede definir una instancia si es "propietario" de la clase de letra 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 algunas veces). Dentro de esas restricciones, donde decide poner la instancia es su propio negocio.

Hay una excepción más: si usted no posee la clase de tipo o el tipo de datos, pero está produciendo un archivo binario y no una biblioteca, eso también está bien.


Entiendo por qué quieres hacer esto, pero desafortunadamente, puede ser solo una ilusión que las clases de Haskell parezcan "abiertas" en la forma en que dices. Muchas personas sienten que la posibilidad de hacer esto es un error en la especificación Haskell, por razones que explicaré a continuación. De todos modos, si realmente no es apropiado para la instancia que necesita declararse en el módulo donde se declara la clase o en el módulo donde se declara el tipo, es probable que sea un signo de que debe usar un tipo newtype o algún otro envoltorio alrededor de tu tipo.

Las razones por las cuales las instancias huérfanas deben evitarse 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 nunca se deben 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 existe un 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 perfectamente funcional que de repente dejaría de compilar 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 el tiempo de ejecución debido a un cambio distante. Podría estar utilizando un método que está asumiendo que proviene de una declaración de instancia determinada, y podría ser reemplazado silenciosamente por una instancia diferente que sea lo suficientemente diferente como para causar que su programa comience a fallar inexplicablemente.

Las personas que desean garantías de que estos problemas nunca les pasarán deben seguir la regla de que si alguien, en algún lugar, alguna vez ha declarado una instancia de una cierta clase para cierto tipo, ninguna otra instancia debe ser declarada nuevamente en ningún programa escrito Por cualquiera. Por supuesto, existe la newtype de utilizar un newtype para declarar una nueva instancia, pero eso siempre es al menos un pequeño inconveniente, y en ocasiones uno importante. Entonces, en este sentido, aquellos que escriben intencionalmente instancias huérfanas son bastante descorteses.

Entonces, ¿qué debería hacerse con respecto a este problema? El campo anti-huérfano 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 ejercer autodisciplina y evitarlos a toda costa.

Como ha 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 hay problema. Algunas instancias huérfanas de otras personas me han molestado lo suficiente como 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 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 podría no ser tan mala. (Y esa misma tentación es la razón por la que algunos en el campo antihuérfano se oponen a mi propuesta).

Mi conclusión a partir de todo esto es que, al menos por el momento, le aconsejaría encarecidamente que evite declarar cualquier instancia huérfana, que sea considerado con los demás si no por otra razón. Usa un newtype .


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. Entonces tengo que proporcionarlos, y entonces son huérfanos.

La idea de que debe ajustar el tipo en un tipo nuevo cuando necesita proporcionar una instancia es una idea con mérito teórico, pero es demasiado tedioso en muchas circunstancias; es el tipo de idea presentada por personas que no escriben el código Haskell para ganarse la vida. :)

Así que adelante y proporcione instancias huérfanas. Ellos son inofensivos.
Si puede bloquear ghc con instancias huérfanas, entonces eso es un error y debe informarse como tal. (El error que ghc tuvo / tiene sobre no detectar instancias múltiples no es tan difícil de solucionar).

Pero tenga en cuenta que en el futuro, alguien más podría agregar la instancia que ya tiene, y es posible que obtenga un error (tiempo de compilación).