2012-04-05 17 views
5

Estoy buscando utilizar las mónadas para representar las consultas SQL y generar el SQL apropiado. A primera vista, esto no es un problema, parece una buena opción. Pero tengo que restringir los tipos, que pueden formar la mónada solo para productos, sin sumas, y no puedo pensar en una forma de colocar tales restricciones.¿Cómo puedo restringir un tipo (tipo?) A los tipos de producto solamente

Quiero utilizar el comprobador de tipos para garantizar que solo se puedan usar tipos representables en SQL.

Podría, supongo, usar la plantilla haskell para derivar las instancias correctas, y negarme a derivar si el tipo no encaja, pero preferiría hacerlo en el nivel de tipo. Menos posibilidades para mí de introducir errores debido a mi ignorancia.

¿Cómo podría hacer esto? En caso afirmativo, podría recomendar algunos ejemplos de lectura y/o código, por favor.

Edit: Gracias, tengo algunos buenos caminos a seguir, que requieren más lectura :) Y aquí viene un largo fin de semana.

Respuesta

8

Desafortunadamente, esto no es realmente posible: un Monad debe ser completamente polimórfico. Es la misma razón por la que no puede hacer que Set sea una mónada (la restricción Ord).

Si puede tratar solo con que el tipo de resultado cumpla con la restricción, entonces podría tener runSQL :: (Product a) => SQL a -> IO a, o similar. En ese caso, solo derivar las instancias apropiadas con Template Haskell es el camino a seguir, o alternativamente, usar el nuevo GHC Generics; plain Haskell no tiene manera de determinar si un tipo está compuesto solo de productos.

Pero sospecho que necesita acceder a toda la estructura del cálculo monádico para traducirlo a SQL. Desafortunadamente, las mónadas no están muy bien equipadas para esto, ya que están conectadas con funciones arbitrarias de Haskell, que no se puede "mirar adentro". Arrows están más cerca, y le permiten hacer más análisis estáticos, pero todavía tienen ese molesto arr que, de nuevo, le permite colarse en funciones arbitrarias de Haskell.

La opción más viable para hacer algo como esto es probablemente escribir una plantilla de empalme Haskell para analizar la sintaxis que desee; puede decir $(sql [| [ (a,b) | a <- table1, b <- table2 |]) y tener sql traducir el AST al SQL correspondiente en tiempo de compilación. Si esa sintaxis es demasiado fea, escriba usando un cuasiquoter con haskell-src-meta, que se vería como [sql| (a, b) | a <- table1, b <- table2 |].

También le puede interesar la extensión generalised arrows, aunque es probable que sea excesivo (y demasiado experimental) para sus propósitos.

+0

Gracias @ehird, eso me da algo en que pensar. Y, sí, creo que restringir el tipo de resultado podría ser suficiente. La clase Product representa una fila SQL, que es el resultado de una consulta SQL SELECT, y para las consultas sin combinaciones, esto debería ser suficiente. Estoy tratando de trabajar para una mini DSL, y mi esperanza inicial era llegar a una etapa, donde el resultado de _runSQL_ es una cadena de consulta aceptable. Entonces algo como '(Product a) => runSQL :: SQL a -> a'. Dejando la ejecución de la consulta a lo que sea que haga un back-end de db. – dikini

+0

Lo sentimos, en la figura anterior, la función def debería decir 'runSQL :: (Producto a) => SQL a -> AST a' para algunos' AST a' – dikini

+0

Las mónadas ya no tienen que ser completamente polimórficas, con [tipos de restricciones ] (http://blog.omega-prime.co.uk/?p=127), ¿verdad? Sin embargo, no sé si estos son aplicables al problema del PO. – leftaroundabout

Cuestiones relacionadas