¿Qué tiene de malo Template Haskell?

252

Parece que Template Haskell es a menudo visto por la comunidad de Haskell como una conveniencia desafortunada. Es difícil poner en palabras exactamente lo que he observado al respecto, pero considere estos pocos ejemplos

He visto varias publicaciones de blog en las que las personas hacen cosas bastante buenas con Template Haskell, lo que permite una sintaxis más bonita que simplemente no sería posible en Haskell regular, así como una tremenda reducción de repeticiones. Entonces, ¿por qué se desprecia a Template Haskell de esta manera? ¿Qué lo hace indeseable? ¿En qué circunstancias se debe evitar Template Haskell y por qué?

Dan Burton
fuente
56
No estoy de acuerdo con el voto para mudarse; Estoy haciendo esta pregunta con el mismo espíritu que pregunté ¿Qué tiene de malo Lazy I / O? y espero ver respuestas de la misma manera. Estoy abierto a reformular la pregunta si eso ayudaría.
Dan Burton
51
@ErikPhilips ¿Por qué no dejas que las personas que frecuentan esta etiqueta decidan si pertenece aquí? Parece que su única interacción con la comunidad de Haskell es dejar sus preguntas.
Gabriel Gonzalez
55
@GabrielGonzalez es obvio con la pregunta actual y responde que no sigue las preguntas frecuentes bajo qué tipo de pregunta no debería hacer aquí: no hay ningún problema real que resolver . La pregunta no tiene un problema específico de código resuelto y solo es de naturaleza conceptual. Y en base a la pregunta debo evitar Haskell plantilla, también cae en el desbordamiento de la pila no es un motor de recomendación .
Erik Philips
27
@ErikPhilips El aspecto del motor de recomendaciones es irrelevante para esta pregunta, porque notará que se refiere a una decisión entre diferentes herramientas (por ejemplo, "dime qué idioma debo usar"). Por el contrario, solo estoy pidiendo una explicación sobre Template Haskell específicamente, y las preguntas frecuentes dicen "si su motivación es" me gustaría que otros me expliquen [en blanco] ", entonces probablemente esté bien". Compare con, por ejemplo, ¿ GOTO todavía se considera dañino?
Dan Burton
29
Votación para reabrir. El hecho de que sea una pregunta de nivel superior no significa que no sea buena.
György Andrasek

Respuestas:

171

Una razón para evitar Template Haskell es que, en general, no es de tipo seguro, lo que va en contra de gran parte del "espíritu de Haskell". Aquí hay algunos ejemplos de esto:

  • No tiene control sobre qué tipo de AST de Haskell generará un código TH, más allá de donde aparecerá; usted puede tener un valor de tipo Exp, pero no sabe si se trata de una expresión que representa un [Char]o una (a -> (forall b . b -> c)), o lo que sea. TH sería más confiable si se pudiera expresar que una función solo puede generar expresiones de cierto tipo, o solo declaraciones de funciones, o solo patrones de coincidencia de constructor de datos, etc.
  • Puede generar expresiones que no compilan. ¿Generaste una expresión que hace referencia a una variable libre fooque no existe? Mala suerte, solo verá eso cuando realmente use su generador de código, y solo bajo las circunstancias que desencadenan la generación de ese código en particular. También es muy difícil realizar pruebas unitarias.

TH también es absolutamente peligroso:

  • El código que se ejecuta en tiempo de compilación puede ser arbitrario IO, incluido el lanzamiento de misiles o el robo de su tarjeta de crédito. No querrás tener que revisar cada paquete de cabal que hayas descargado en busca de exploits TH.
  • TH puede acceder a funciones y definiciones de "módulo privado", rompiendo completamente la encapsulación en algunos casos.

Luego, hay algunos problemas que hacen que las funciones de TH sean menos divertidas de usar como desarrollador de bibliotecas:

  • El código TH no siempre es composable. Digamos que alguien hace un generador para lentes, y la mayoría de las veces, ese generador estará estructurado de tal manera que solo pueda ser llamado directamente por el "usuario final" y no por otro código TH, por ejemplo tomando una lista de constructores de tipos para generar lentes como parámetro. Es complicado generar esa lista en código, mientras que el usuario solo tiene que escribir generateLenses [''Foo, ''Bar].
  • Los desarrolladores ni siquiera saben que el código TH se puede componer. ¿Sabías que puedes escribir forM_ [''Foo, ''Bar] generateLens? Qes solo una mónada, por lo que puede usar todas las funciones habituales en él. Algunas personas no saben esto, y debido a eso, crean múltiples versiones sobrecargadas de esencialmente las mismas funciones con la misma funcionalidad, y estas funciones conducen a un cierto efecto de hinchazón. Además, la mayoría de las personas escribe sus generadores en la Qmónada incluso cuando no tienen que hacerlo, que es como escribir bla :: IO Int; bla = return 3; le está dando a una función más "entorno" de lo que necesita, y los clientes de la función deben proporcionar ese entorno como efecto de eso.

Finalmente, hay algunas cosas que hacen que las funciones de TH sean menos divertidas de usar como usuario final:

  • Opacidad. Cuando una función TH tiene tipo Q Dec, puede generar absolutamente cualquier cosa en el nivel superior de un módulo, y no tiene absolutamente ningún control sobre lo que se generará.
  • Monolitismo No puede controlar cuánto genera una función TH a menos que el desarrollador lo permita; si encuentra una función que genera una interfaz de base de datos y una interfaz de serialización JSON, no puede decir "No, solo quiero la interfaz de base de datos, gracias; rodaré mi propia interfaz JSON"
  • Tiempo de ejecución. El código TH tarda un tiempo relativamente largo en ejecutarse. El código se interpreta nuevamente cada vez que se compila un archivo y, a menudo, el código TH en ejecución requiere una tonelada de paquetes que deben cargarse. Esto ralentiza considerablemente el tiempo de compilación.
dflemstr
fuente
44
Agregue a esto el hecho de que template-haskell ha sido históricamente muy poco documentado. (Aunque acabo de mirar de nuevo y parece que las cosas están al menos marginalmente mejor ahora.) Además, para entender template-haskell, esencialmente tienes que entender la gramática del lenguaje Haskell, que impone una cierta cantidad de complejidad (no es un Esquema). Estas dos cosas me ayudaron intencionalmente a no molestarme en comprender TH cuando era un principiante de Haskell.
mightybyte
14
¡No olvide que el uso de Template Haskell de repente significa que el orden de las declaraciones es importante! TH simplemente no está integrado tan firmemente como uno podría esperar dado el pulido suave de Haskell (ya sea 1.4, '98, 2010 o incluso Glasgow).
Thomas M. DuBuisson
13
Puede razonar sobre Haskell sin demasiada dificultad, no existe tal garantía sobre Template Haskell.
augustss
15
¿Y qué pasó con la promesa de Oleg de una alternativa de tipo seguro a TH? Me refiero a su trabajo basado en su trabajo "Finalmente sin etiquetas, parcialmente evaluado" y más de sus notas aquí . Parecía tan prometedor cuando lo anunciaron y luego nunca escuché otra palabra al respecto.
Gabriel Gonzalez
53

Esta es únicamente mi propia opinión.

  • Es feo de usar. $(fooBar ''Asdf)simplemente no se ve bien. Superficial, claro, pero contribuye.

  • Es aún más feo escribir. Las citas a veces funcionan, pero muchas veces tienes que hacer un injerto y plomería AST manual. La API es grande y difícil de manejar, siempre hay muchos casos que no le importan pero que aún necesita despachar, y los casos que sí le interesan tienden a estar presentes en múltiples formas similares pero no idénticas (datos versus tipo nuevo, registro -constructor versus constructores normales, etc.). Es aburrido y repetitivo escribir y lo suficientemente complicado como para no ser mecánico. La propuesta de reforma aborda algo de esto (haciendo que las citas sean más aplicables).

  • La restricción de escenario es el infierno. No poder empalmar las funciones definidas en el mismo módulo es la parte más pequeña del mismo: la otra consecuencia es que si tiene un empalme de nivel superior, todo lo que esté después en el módulo estará fuera del alcance de cualquier cosa anterior. Otros lenguajes con esta propiedad (C, C ++) lo hacen viable al permitirle reenviar declaraciones, pero Haskell no. Si necesita referencias cíclicas entre declaraciones empalmadas o sus dependencias y dependientes, generalmente está jodido.

  • Es indisciplinado. Lo que quiero decir con esto es que la mayoría de las veces cuando expresas una abstracción, hay algún tipo de principio o concepto detrás de esa abstracción. Para muchas abstracciones, el principio detrás de ellas se puede expresar en sus tipos. Para las clases de tipos, a menudo puede formular leyes que las instancias deben obedecer y los clientes pueden asumir. Si utiliza la nueva función genérica de GHC para abstraer la forma de una declaración de instancia sobre cualquier tipo de datos (dentro de los límites), puede decir "para los tipos de suma, funciona así, para los tipos de productos, funciona así". Template Haskell, por otro lado, son solo macros. No es la abstracción a nivel de ideas, sino la abstracción a nivel de AST, que es mejor, pero solo modestamente, que la abstracción a nivel de texto plano. *

  • Te vincula a GHC. En teoría, otro compilador podría implementarlo, pero en la práctica dudo que esto suceda alguna vez. (Esto está en contraste con varias extensiones de sistema de tipo que, aunque solo podrían ser implementadas por GHC en este momento, fácilmente podría imaginar ser adoptado por otros compiladores en el futuro y eventualmente estandarizado).

  • La API no es estable. Cuando se agregan nuevas funciones de lenguaje a GHC y el paquete template-haskell se actualiza para admitirlas, esto a menudo implica cambios incompatibles con versiones anteriores de los tipos de datos TH. Si desea que su código TH sea compatible con más de una versión de GHC, debe tener mucho cuidado y posiblemente usarlo CPP.

  • Hay un principio general de que debe usar la herramienta adecuada para el trabajo y la más pequeña que sea suficiente, y en esa analogía, Template Haskell es algo así . Si hay una manera de hacerlo que no sea Template Haskell, generalmente es preferible.

La ventaja de Template Haskell es que puedes hacer cosas que no podrías hacer de otra manera, y es muy importante. La mayoría de las veces, las cosas para las que se usa TH solo se podrían hacer si se implementaran directamente como características del compilador. TH es extremadamente beneficioso tener ambos porque le permite hacer estas cosas y porque le permite crear prototipos de posibles extensiones de compilador de una manera mucho más liviana y reutilizable (vea los diversos paquetes de lentes, por ejemplo).

Para resumir por qué creo que hay sentimientos negativos hacia Template Haskell: resuelve muchos problemas, pero para cualquier problema que resuelva, parece que debería haber una solución mejor, más elegante y disciplinada más adecuada para resolver ese problema, uno que no resuelva el problema generando automáticamente la placa de repeticiones, pero eliminando la necesidad de tener la placa de repeticiones.

* Aunque a menudo siento que CPPtiene una mejor relación potencia / peso para esos problemas que puede resolver.

EDITAR 23-04-14: A lo que intentaba llegar con frecuencia en lo anterior, y que recientemente he llegado exactamente, es que hay una distinción importante entre abstracción y deduplicación. La abstracción adecuada a menudo resulta en la deduplicación como un efecto secundario, y la duplicación a menudo es un signo revelador de abstracción inadecuada, pero esa no es la razón por la cual es valioso. La abstracción adecuada es lo que hace que el código sea correcto, comprensible y mantenible. La deduplicación solo lo hace más corto. Template Haskell, como las macros en general, es una herramienta para la deduplicación.

glaebhoerl
fuente
"CPP tiene una mejor relación potencia / peso para los problemas que puede resolver". En efecto. Y en C99 puede resolver lo que quieras. Considere estos: COS , Caos . Tampoco entiendo por qué la gente piensa que la generación de AST es mejor. Es solo más ambigüedad y menos ortogonal a otras características del lenguaje
Britton Kerin
31

Me gustaría abordar algunos de los puntos que plantea dflemstr.

No creo que el hecho de que no puedas escribir TH sea tan preocupante. ¿Por qué? Porque incluso si hay un error, seguirá siendo tiempo de compilación. No estoy seguro de si esto fortalece mi argumento, pero esto es similar en espíritu a los errores que recibe al usar plantillas en C ++. Sin embargo, creo que estos errores son más comprensibles que los errores de C ++, ya que obtendrá una versión bastante impresa del código generado.

Si una expresión TH / cuasi-quoter hace algo tan avanzado que las esquinas difíciles pueden esconderse, ¿entonces quizás sea desaconsejado?

Rompo esta regla bastante con las cuasi-citas en las que he estado trabajando últimamente (usando haskell-src-exts / meta) - https://github.com/mgsloan/quasi-extras/tree/master/examples . Sé que esto introduce algunos errores, como no poder empalmar en las comprensiones de la lista generalizada. Sin embargo, creo que hay una buena posibilidad de que algunas de las ideas en http://hackage.haskell.org/trac/ghc/blog/Template%20Haskell%20Proposal terminen en el compilador. Hasta entonces, las bibliotecas para analizar los árboles de Haskell a TH son una aproximación casi perfecta.

Con respecto a la velocidad de compilación / dependencias, podemos usar el paquete "zeroth" para incorporar el código generado. Esto es al menos agradable para los usuarios de una biblioteca determinada, pero no podemos hacer mucho mejor para el caso de editar la biblioteca. ¿Pueden las dependencias TH hinchar binarios generados? Pensé que dejaba de lado todo lo que el código compilado no hace referencia.

La restricción / división de etapas de los pasos de compilación del módulo Haskell es una mierda.

Opacidad RE: Esto es lo mismo para cualquier función de biblioteca que llame. No tienes control sobre lo que hará Data.List.groupBy. Simplemente tiene una "garantía" / convención razonable de que los números de versión le dicen algo sobre la compatibilidad. Es una cuestión de cambio algo diferente cuando.

Aquí es donde vale la pena usar zeroth: ya está versionando los archivos generados, por lo que siempre sabrá cuándo ha cambiado la forma del código generado. Sin embargo, mirar las diferencias puede ser un poco retorcido para grandes cantidades de código generado, por lo que ese es un lugar donde una mejor interfaz de desarrollador sería útil.

Monolitismo RE: Ciertamente puede postprocesar los resultados de una expresión TH, utilizando su propio código de tiempo de compilación. No sería mucho código filtrar por tipo / nombre de declaración de nivel superior. Diablos, podrías imaginar escribir una función que haga esto genéricamente. Para modificar / des-monolitizar cuasiquoters, puede combinar patrones en "QuasiQuoter" y extraer las transformaciones utilizadas, o hacer una nueva en términos de la anterior.

mgsloan
fuente
1
Con respecto a la opacidad / monolitismo: por supuesto, puede recorrer [Dec]ay eliminar cosas que no desea, pero supongamos que la función lee un archivo de definición externo al generar la interfaz JSON. El hecho de que no use eso Decno hace que el generador deje de buscar el archivo de definición, lo que hace que la compilación falle. Por esa razón, sería bueno tener una versión más restrictiva de la Qmónada que le permitiera generar nuevos nombres (y cosas así) pero no permitir IO, de modo que, como usted dice, uno podría filtrar sus resultados / hacer otras composiciones .
dflemstr
1
Estoy de acuerdo, ¡debería haber una versión de Q / cita que no sea IO! Esto también ayudaría a resolver - stackoverflow.com/questions/7107308/… . Una vez que se asegura de que el código en tiempo de compilación no hace nada inseguro, parece que podría ejecutar el verificador de seguridad en el código resultante y asegurarse de que no haga referencia a cosas privadas.
mgsloan
1
¿Alguna vez escribiste tu contraargumento? Todavía estoy esperando tu enlace prometido. Para aquellos que buscan el contenido anterior de esta respuesta: stackoverflow.com/revisions/10859441/1 , y para aquellos que pueden ver el contenido eliminado: stackoverflow.com/revisions/10913718/6
Dan Burton
1
¿Existe una versión más actualizada de zeroth que la del pirateo? Ese (y el repositorio de Darcs) se actualizaron por última vez en 2009. Ambas copias no se compilan con un ghc actual (7.6).
aavogt
1
@aavogt tgeeky estaba trabajando para arreglarlo, pero no creo que haya terminado: github.com/technogeeky/zeroth Si nadie lo hace / hace algo para esto, eventualmente lo solucionaré.
mgsloan
16

Esta respuesta responde a los problemas planteados por illissius, punto por punto:

  • Es feo de usar. $ (fooBar '' Asdf) simplemente no se ve bien. Superficial, claro, pero contribuye.

Estoy de acuerdo. Siento que $ () se eligió para que pareciera que era parte del lenguaje, utilizando la paleta de símbolos familiar de Haskell. Sin embargo, eso es exactamente lo que usted / no / quiere en los símbolos utilizados para su empalme de macro. Definitivamente se mezclan demasiado, y este aspecto cosmético es bastante importante. Me gusta la apariencia de {{}} para los empalmes, porque son bastante distintos visualmente.

  • Es aún más feo escribir. Las citas a veces funcionan, pero muchas veces tienes que hacer un injerto y plomería AST manual. La [API] [1] es grande y difícil de manejar, siempre hay muchos casos que no le importan pero que aún necesita despachar, y los casos que le interesan tienden a estar presentes en múltiples formas similares pero no idénticas (datos vs. newtype, estilo de registro vs. constructores normales, etc.). Es aburrido y repetitivo escribir y lo suficientemente complicado como para no ser mecánico. La [propuesta de reforma] [2] aborda algo de esto (haciendo que las citas sean más ampliamente aplicables).

Sin embargo, también estoy de acuerdo con esto, como lo observan algunos de los comentarios en "Nuevas direcciones para TH", la falta de una buena cita de AST lista para usar no es un defecto crítico. En este paquete WIP, busco abordar estos problemas en forma de biblioteca: https://github.com/mgsloan/quasi-extras . Hasta ahora, permito empalmar en algunos lugares más de lo habitual y puedo hacer coincidir patrones en AST.

  • La restricción de escenario es el infierno. No poder empalmar las funciones definidas en el mismo módulo es la parte más pequeña del mismo: la otra consecuencia es que si tiene un empalme de nivel superior, todo lo que esté después en el módulo estará fuera del alcance de cualquier cosa anterior. Otros lenguajes con esta propiedad (C, C ++) lo hacen viable al permitirle reenviar declaraciones, pero Haskell no. Si necesita referencias cíclicas entre declaraciones empalmadas o sus dependencias y dependientes, generalmente está jodido.

Me he encontrado con el problema de que las definiciones cíclicas de TH son imposibles antes ... Es bastante molesto. Hay una solución, pero es fea: envolver las cosas involucradas en la dependencia cíclica en una expresión TH que combine todas las declaraciones generadas. Uno de estos generadores de declaraciones podría ser un cuasi-cita que acepta el código Haskell.

  • No tiene principios. Lo que quiero decir con esto es que la mayoría de las veces cuando expresas una abstracción, hay algún tipo de principio o concepto detrás de esa abstracción. Para muchas abstracciones, el principio detrás de ellas se puede expresar en sus tipos. Cuando define una clase de tipo, a menudo puede formular leyes que las instancias deben obedecer y los clientes pueden asumir. Si utiliza la [nueva función genérica] de GHC [3] para abstraer la forma de una declaración de instancia sobre cualquier tipo de datos (dentro de los límites), puede decir "para tipos de suma, funciona así, para tipos de productos, funciona así ". Pero Template Haskell es simplemente macros tontos. No es la abstracción a nivel de ideas, sino la abstracción a nivel de AST, que es mejor, pero solo modestamente, que la abstracción a nivel de texto sin formato.

Solo no tiene principios si haces cosas sin principios con él. La única diferencia es que con los mecanismos de abstracción implementados por el compilador, tiene más confianza en que la abstracción no tiene fugas. ¡Quizás democratizar el diseño del lenguaje suena un poco aterrador! Los creadores de las bibliotecas TH necesitan documentar bien y definir claramente el significado y los resultados de las herramientas que proporcionan. Un buen ejemplo de TH basado en principios es el paquete de derivación : http://hackage.haskell.org/package/derive : utiliza un DSL tal que el ejemplo de muchas de las derivaciones / especifica / la derivación real.

  • Te vincula a GHC. En teoría, otro compilador podría implementarlo, pero en la práctica dudo que esto suceda alguna vez. (Esto está en contraste con varias extensiones de sistema de tipo que, aunque solo podrían ser implementadas por GHC en este momento, fácilmente podría imaginar ser adoptado por otros compiladores en el futuro y eventualmente estandarizado).

Ese es un punto bastante bueno: la API TH es bastante grande y torpe. Volver a implementarlo parece que podría ser difícil. Sin embargo, en realidad solo hay algunas maneras de dividir el problema de representar AST de Haskell. Me imagino que copiar los TH ADT y escribir un convertidor en la representación interna de AST te ayudaría mucho. Esto sería equivalente al esfuerzo (no insignificante) de crear haskell-src-meta. También se podría volver a implementar simplemente imprimiendo bonito el TH AST y utilizando el analizador interno del compilador.

Si bien podría estar equivocado, no veo a TH como una extensión de compilador tan complicada, desde una perspectiva de implementación. Este es en realidad uno de los beneficios de "mantenerlo simple" y no tener la capa fundamental como un sistema de plantillas teóricamente atractivo y verificable estáticamente.

  • La API no es estable. Cuando se agregan nuevas funciones de lenguaje a GHC y el paquete template-haskell se actualiza para admitirlas, esto a menudo implica cambios incompatibles con versiones anteriores de los tipos de datos TH. Si desea que su código TH sea compatible con más de una versión de GHC, debe tener mucho cuidado y posiblemente usarlo CPP.

Este también es un buen punto, pero algo dramático. Si bien ha habido adiciones de API últimamente, no han sido ampliamente inductoras de roturas. Además, creo que con la cita superior de AST que mencioné anteriormente, la API que realmente necesita ser utilizada puede reducirse sustancialmente. Si ninguna construcción / coincidencia necesita funciones distintas, y en su lugar se expresan como literales, entonces la mayor parte de la API desaparece. Además, el código que escriba se transferirá más fácilmente a las representaciones de AST para idiomas similares a Haskell.


En resumen, creo que TH es una herramienta poderosa, semi-descuidada. Menos odio podría conducir a un ecosistema de bibliotecas más animado, fomentando la implementación de más prototipos de características del lenguaje. Se ha observado que TH es una herramienta dominada, que puede permitirte / hacer / casi cualquier cosa. ¡Anarquía! Bueno, es mi opinión que este poder puede permitirle superar la mayoría de sus limitaciones y construir sistemas capaces de enfoques de metaprogramación bastante basados ​​en principios. Vale la pena usar hacks feos para simular la implementación "adecuada", ya que de esta manera el diseño de la implementación "adecuada" se irá aclarando gradualmente.

En mi versión ideal personal de nirvana, gran parte del lenguaje se movería del compilador a bibliotecas de esta variedad. El hecho de que las características se implementen como bibliotecas no influye mucho en su capacidad de abstraer fielmente.

¿Cuál es la respuesta típica de Haskell al código repetitivo? Abstracción. ¿Cuáles son nuestras abstracciones favoritas? Funciones y clases de tipos!

Las clases de tipos nos permiten definir un conjunto de métodos, que luego se pueden utilizar en todo tipo de funciones genéricas en esa clase. Sin embargo, aparte de esto, la única forma en que las clases ayudan a evitar repeticiones es ofreciendo "definiciones predeterminadas". ¡Ahora aquí hay un ejemplo de una característica sin principios!

  • Los conjuntos de enlace mínimos no son declarables / compilador comprobable. Esto podría conducir a definiciones inadvertidas que rinden fondo debido a la recursividad mutua.

  • A pesar de la gran comodidad y potencia que esto produciría, no puede especificar valores predeterminados de superclase, debido a instancias huérfanas http://lukepalmer.wordpress.com/2009/01/25/a-world-without-orphans/ Esto nos permitiría arreglar el jerarquía numérica con gracia!

  • Buscar las capacidades de TH para los valores predeterminados de los métodos condujo a http://www.haskell.org/haskellwiki/GHC.Generics . Si bien esto es algo genial, mi única experiencia en la depuración de código usando estos genéricos fue casi imposible, debido al tamaño del tipo inducido y ADT tan complicado como un AST. https://github.com/mgsloan/th-extra/commit/d7784d95d396eb3abdb409a24360beb03731c88c

    En otras palabras, esto fue después de las características proporcionadas por TH, pero tuvo que levantar un dominio completo del lenguaje, el lenguaje de construcción, en una representación de sistema de tipos. Si bien puedo ver que funciona bien para su problema común, para los complejos, parece propenso a producir una pila de símbolos mucho más aterrador que la piratería informática TH.

    TH le proporciona un cálculo en tiempo de compilación a nivel de valor del código de salida, mientras que los genéricos lo obligan a levantar la parte de coincidencia / recursión de patrones del código en el sistema de tipos. Si bien esto restringe al usuario de algunas maneras bastante útiles, no creo que la complejidad valga la pena.

Creo que el rechazo de TH y la metaprogramación de tipo lisp condujo a la preferencia hacia cosas como los valores predeterminados del método en lugar de declaraciones de instancias más flexibles y de macroexpansión. La disciplina de evitar cosas que podrían conducir a resultados imprevistos es sabia, sin embargo, no debemos ignorar que el sistema de tipos capaz de Haskell permite una metaprogramación más confiable que en muchos otros entornos (verificando el código generado).

mgsloan
fuente
44
Esta respuesta no se sostiene por sí sola muy bien: estás haciendo un montón de referencias a otra respuesta que tengo que buscar y encontrar antes de que pueda leer la tuya correctamente.
Ben Millwood
Es verdad. A pesar de todo, intenté aclarar de qué se hablaba. Tal vez editaré los puntos de illisuis en línea.
mgsloan
Debo decir que tal vez "sin principios" es una palabra más fuerte de lo que debería haber usado, con algunas connotaciones que no tenía la intención, ¡ciertamente no es sin escrúpulos! Esa viñeta fue con la que tuve más problemas, porque tengo este sentimiento o idea no formada en mi cabeza y problemas para ponerlo en palabras, y "sin principios" fue una de las palabras que entendí en algún lugar cercano. "Disciplinado" probablemente esté más cerca. Al no tener una formulación clara y sucinta, busqué algunos ejemplos. Si alguien puede explicarme más claramente lo que probablemente quise decir, ¡lo agradecería!
glaebhoerl
(También creo que podría haber cambiado la restricción puesta en escena y cotizaciones feas-a-escritura.)
glaebhoerl
1
Doh! ¡Gracias por señalar eso! Fijo. Sí, indisciplinado probablemente sería una mejor manera de decirlo. Creo que la distinción aquí es realmente entre mecanismos de abstracción "incorporados" y, por lo tanto, "bien entendidos", versus mecanismos de abstracción "ad-hoc" o definidos por el usuario. Supongo que lo que quiero decir es que posiblemente podría definir una biblioteca TH que implementara algo bastante similar al despacho de la clase de tipos (aunque en tiempo de compilación no en tiempo de ejecución)
mgsloan
8

Un problema bastante pragmático con Template Haskell es que solo funciona cuando el intérprete de bytecode de GHC está disponible, lo cual no es el caso en todas las arquitecturas. Entonces, si su programa usa Template Haskell o se basa en bibliotecas que lo usan, no se ejecutará en máquinas con una CPU ARM, MIPS, S390 o PowerPC.

Esto es relevante en la práctica: git-annex es una herramienta escrita en Haskell que tiene sentido para ejecutarse en máquinas que se preocupan por el almacenamiento, estas máquinas a menudo tienen CPU no i386. Personalmente, ejecuto git-annex en un NSLU 2 (32 MB de RAM, CPU de 266MHz; ¿sabías que Haskell funciona bien en dicho hardware?) Si usaría Template Haskell, esto no es posible.

(La situación sobre GHC en ARM está mejorando mucho en estos días y creo que 7.4.2 incluso funciona, pero el punto sigue en pie).

Joachim Breitner
fuente
1
"Template Haskell se basa en el compilador e intérprete de bytecode incorporado de GHC para ejecutar las expresiones de empalme". - haskell.org/ghc/docs/7.6.2/html/users_guide/…
Joachim Breitner
2
ah, eso, no, TH no funcionará sin el intérprete de bytecode, pero eso es distinto de (aunque relevante para) ghci. No me sorprendería si hubiera una relación perfecta entre la disponibilidad de ghci y la disponibilidad del intérprete de bytecode, dado que ghci depende del intérprete de bytecode, pero el problema es la falta del intérprete de bytecode, no la falta de ghci específicamente.
muhmuhten
1
(por cierto, ghci tiene un soporte increíblemente pobre para el uso interactivo de TH. try ghci -XTemplateHaskell <<< '$(do Language.Haskell.TH.runIO $ (System.Random.randomIO :: IO Int) >>= print; [| 1 |] )')
muhmuhten
Ok, con ghci me refería a la capacidad de GHC para interpretar (en lugar de compilar) el código, independientemente de si el código viene de forma interactiva en el binario ghci o desde el empalme TH.
Joachim Breitner
6

¿Por qué es malo TH? Para mí, todo se reduce a esto:

Si necesita producir tanto código repetitivo que intenta utilizar TH para generarlo automáticamente, ¡ lo está haciendo mal!

Piénsalo. La mitad del atractivo de Haskell es que su diseño de alto nivel le permite evitar grandes cantidades de código inútil que debe escribir en otros idiomas. Si necesita generar código en tiempo de compilación, básicamente está diciendo que su idioma o el diseño de su aplicación le han fallado. Y a los programadores no nos gusta fallar.

A veces, por supuesto, es necesario. Pero a veces puede evitar necesitar TH simplemente siendo un poco más inteligente con sus diseños.

(La otra cosa es que TH es de nivel bastante bajo. No hay un gran diseño de alto nivel; muchos detalles de implementación interna de GHC están expuestos. Y eso hace que la API sea propensa a cambiar ...)

Orquídea matemática
fuente
No creo que signifique que su idioma o aplicación le haya fallado, especialmente si consideramos QuasiQuotes. Siempre hay compensaciones cuando se trata de sintaxis. Algunas sintaxis describen mejor un determinado dominio, por lo que a veces es posible que desee cambiar a otra sintaxis. QuasiQuotes le permite cambiar elegantemente entre sintaxis. Esto es muy poderoso y lo usan Yesod y otras aplicaciones. Poder escribir código de generación HTML usando una sintaxis que se siente como HTML es una característica sorprendente.
CoolCodeBro
@CoolCodeBro Sí, la cuasi cita es bastante agradable, y supongo que es un poco ortogonal a TH. (Obviamente se implementa sobre TH.) Estaba pensando más en usar TH para generar instancias de clase, o construir funciones de múltiples arities, o algo así.
MathematicalOrchid