2010-03-05 13 views
15

Estoy pensando en formas de utilizar el sistema de tipos de Haskell para imponer la modularidad en un programa. Por ejemplo, si tengo una aplicación web, tengo curiosidad de saber si hay una manera de separar todo el código de la base de datos del código CGI del código del sistema de archivos del código puro.Uso del sistema de tipos de Haskell para forzar la modularidad

Por ejemplo, yo estoy imaginando una mónada DB, por lo que podría escribir funciones como:

countOfUsers :: DB Int 
countOfUsers = select "count(*) from users" 

me gustaría que sea imposible utilizar efectos secundarios distintos a los que el apoyo de la mónada DB. Me estoy imaginando una mónada de nivel superior que estaría limitada a manejadores de URL directos y podría componer llamadas a la mónada DB y a la mónada IO.

¿Esto es posible? ¿Es esto sabio?

actualización: Terminé lograr esto con Scala en lugar de Haskell: http://moreindirection.blogspot.com/2011/08/implicit-environment-pattern.html

Respuesta

13

estoy imaginando una mónada de más alto nivel que estaría limitado a los manejadores de URL directos y podría componer llamadas a la mónada DB y a la mónada IO.

Sin duda puede lograr esto, y obtener garantías estáticas muy fuertes sobre la separación de los componentes.

En su forma más simple, desea una món IO restringida. Usando algo así como una técnica de "contaminación", puede crear un conjunto de operaciones de E/S levantadas en un envoltorio simple, luego usar el sistema de módulos para ocultar los constructores subyacentes para los tipos.

De esta forma, solo podrá ejecutar código CGI en un contexto CGI y código DB en un contexto DB. Hay muchos ejemplos en Hackage.

Otra forma es construir un intérprete para las acciones, y luego usar constructores de datos para describir cada operación primitiva que desee. Las operaciones deben seguir formando una mónada, y puede utilizar la notación do, pero en su lugar se construirá una estructura de datos que describa las acciones a ejecutar, que luego se ejecutarán de forma controlada a través de un intérprete.

Esto le da quizás más introspección de la que necesita en casos típicos, pero el enfoque le da plena capacidad para inspeccionar el código de usuario antes de ejecutarlo.

+0

¡Gracias, Don! La primera solución suena como lo que estoy buscando. ¿Conoces algún paquete específico que use esta técnica, o buenos términos para google for ("mónada IO restringida" no apareció mucho)? – Bill

+1

Un buen ejemplo del concepto de 'mónada de contaminación', http://blog.sigfpe.com/2007/04/trivial-monad.html –

+0

Gracias. Si elijo utilizar el patrón "mónada contaminada" para mi mónada de base de datos, ¿qué debo hacer para extraer los datos de la mónada de base de datos? ¿Mi controlador de acción HTTP tiene que usar un transformador de mónada con DB en él? – Bill

4

Gracias por esta pregunta!

Trabajé en un marco web cliente/servidor que usaba mónadas para distinguir entre diferentes entornos de ejecución. Las más obvias eran del lado del cliente y del lado del servidor, sino que también permite que usted pueda escribir tanto del lado del código (que podría ejecutar en el cliente y el servidor, ya que no contiene ninguna característica especial) y también asíncrono del lado del cliente que se utilizó para escribir código no bloqueante en el cliente (esencialmente una mónada de continuación en el lado del cliente). Esto suena bastante relacionado con su idea de distinguir entre código CGI y código DB.

Éstos son algunos recursos sobre mi proyecto:

  • Slides de una presentación que hice sobre el proyecto
  • Draft paper que escribí con Don Syme
  • Y también escribí mi Bachelor thesis sobre este tema (que es bastante largo)

Creo que este es un enfoque interesante y puede darte una garantía interesante s sobre el código. Sin embargo, hay algunas preguntas difíciles. Si tiene una función del lado del servidor que toma un int y devuelve int, entonces ¿cuál debería ser el tipo de esta función? En mi proyecto, he utilizado int -> int server (pero también puede ser posible utilizar server (int -> int).

Si usted tiene un par de funciones de este tipo, entonces no es tan sencillo para componerlos. En lugar de escribir goo (foo (bar 1)), necesita para escribir el siguiente código:.

do b <- bar 1 
    f <- foo b 
    return goo f 

se puede escribir lo mismo usando algunos combinadores, pero mi punto es que la composición es un poco menos elegante

+0

'import Control.Monad; (goo <= ephemient

+0

Sí, esto fue peor en F # donde también quería escribir llamadas a métodos como 'o.Bar() .Foo() .Goo()' y no hay forma de hacerlo con los combinadores. Usar '<= <' en Haskell parece correcto, pero aún no es perfecto. –

5

Creo que hay un tercer camino más allá de los dos Don Stewart mencionó, que incluso puede ser más simple:

class Monad m => MonadDB m where 
    someDBop1 :: String -> m() 
    someDBop2 :: String -> m [String] 

class Monad m => MonadCGI m where 
    someCGIop1 :: ... 
    someCGIop2 :: ... 

functionWithOnlyDBEffects :: MonadDB m => Foo -> Bar -> m() 
functionWithOnlyDBEffects = ... 

functionWithDBandCGIEffects :: (MonadDB m, MonadCGI m) => Baz -> Quux -> m() 
functionWithDBandCGIEffects = ... 

instance MonadDB IO where 
    someDBop1 = ... 
    someDBop2 = ... 

instance MonadCGI IO where 
    someCGIop1 = ... 
    someCGIop2 = ... 

La idea es muy simple que defina el tipo clases para los distintos subconjuntos de operaciones que desea separar y luego parametrice sus funciones usándolos. Incluso si la única mónada concreta que alguna vez hace una instancia de las clases es IO, las funciones parametrizadas en cualquier MonadDB solo podrán usar las operaciones de MonadDB (y las que se construyan a partir de ellas), de modo que se logrará el resultado deseado. Y en una función "puede hacer cualquier cosa" en la mónada IO, puede usar las operaciones MonadDB y MonadCGI sin problemas, porque IO es una instancia.

(Por supuesto, puede definir otros casos, si así lo desea. Ones para levantar las operaciones a través de varios transformadores mónada sería sencillo, y creo que no hay realmente nada que nos impida casos de escritura para la "envoltura" y Mónadas de "intérprete" que Don Stewart menciona, combinando así los enfoques, aunque no estoy seguro de si hay una razón por la que desearía).

Cuestiones relacionadas