En este tema, aprenderemos cómo interactuar con bases de datos desde Haskell. Cubriremos los conceptos básicos de la conexión a una base de datos, la ejecución de consultas y la manipulación de resultados. Utilizaremos la biblioteca persistent para facilitar estas tareas.
Contenido
Introducción a Persistent
persistent es una biblioteca de Haskell que proporciona una forma sencilla y segura de interactuar con bases de datos. Soporta varios sistemas de bases de datos, incluyendo SQLite, PostgreSQL y MySQL.
Ventajas de usar Persistent
- Seguridad de tipos: Las consultas y operaciones están fuertemente tipadas.
- Abstracción: Proporciona una capa de abstracción sobre SQL.
- Facilidad de uso: Simplifica la definición de esquemas y la ejecución de consultas.
Configuración del Entorno
Para comenzar, necesitamos instalar las bibliotecas necesarias. Asegúrate de tener cabal o stack instalado.
Instalación con Cabal
Instalación con Stack
Añade las siguientes dependencias a tu archivo stack.yaml:
Y luego ejecuta:
Definición de Esquemas
En persistent, definimos esquemas utilizando una sintaxis especial en un archivo de plantilla. Aquí hay un ejemplo de cómo definir un esquema para una tabla de usuarios:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
import Database.Persist.TH
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
User
name String
age Int
deriving Show
|]Explicación del Código
- GADTs, GeneralizedNewtypeDeriving, etc.: Extensiones de Haskell necesarias para
persistent. - share: Función que genera el código necesario para interactuar con la base de datos.
- mkPersist, mkMigrate: Funciones que crean las instancias necesarias para el esquema.
- persistLowerCase: Quasi-quote que define el esquema.
Operaciones Básicas de CRUD
Conexión a la Base de Datos
Primero, necesitamos establecer una conexión a la base de datos. Aquí hay un ejemplo de cómo conectarse a una base de datos SQLite:
import Database.Persist.Sqlite
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Logger (runStdoutLoggingT)
main :: IO ()
main = runStdoutLoggingT $ withSqliteConn "test.db" $ \backend -> liftIO $ do
runSqlConn (runMigration migrateAll) backendCrear (Create)
Para insertar un nuevo usuario en la base de datos:
import Database.Persist
main :: IO ()
main = runStdoutLoggingT $ withSqliteConn "test.db" $ \backend -> liftIO $ do
runSqlConn (runMigration migrateAll) backend
runSqlConn (insert $ User "Alice" 30) backendLeer (Read)
Para leer usuarios de la base de datos:
main :: IO ()
main = runStdoutLoggingT $ withSqliteConn "test.db" $ \backend -> liftIO $ do
runSqlConn (runMigration migrateAll) backend
users <- runSqlConn (selectList [] []) backend
liftIO $ print (users :: [Entity User])Actualizar (Update)
Para actualizar un usuario existente:
main :: IO ()
main = runStdoutLoggingT $ withSqliteConn "test.db" $ \backend -> liftIO $ do
runSqlConn (runMigration migrateAll) backend
userId <- runSqlConn (insert $ User "Alice" 30) backend
runSqlConn (update userId [UserAge =. 31]) backendEliminar (Delete)
Para eliminar un usuario:
main :: IO ()
main = runStdoutLoggingT $ withSqliteConn "test.db" $ \backend -> liftIO $ do
runSqlConn (runMigration migrateAll) backend
userId <- runSqlConn (insert $ User "Alice" 30) backend
runSqlConn (delete userId) backendEjemplo Práctico
Vamos a crear un pequeño programa que permita agregar, listar y eliminar usuarios desde la línea de comandos.
Código Completo
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE OverloadedStrings #-}
import Database.Persist
import Database.Persist.Sqlite
import Database.Persist.TH
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Logger (runStdoutLoggingT)
import System.Environment (getArgs)
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
User
name String
age Int
deriving Show
|]
main :: IO ()
main = do
args <- getArgs
case args of
("add":name:age:_) -> runDb $ insert_ (User name (read age))
("list":_) -> runDb $ selectList [] [] >>= liftIO . print
("delete":name:_) -> runDb $ deleteWhere [UserName ==. name]
_ -> putStrLn "Usage: add <name> <age> | list | delete <name>"
runDb :: SqlPersistT (LoggingT IO) a -> IO a
runDb query = runStdoutLoggingT $ withSqliteConn "test.db" $ \backend -> runSqlConn query backendExplicación del Código
- main: Lee los argumentos de la línea de comandos y ejecuta la operación correspondiente.
- runDb: Función auxiliar para ejecutar consultas en la base de datos.
Ejercicios Prácticos
- Ejercicio 1: Modifica el programa para que también permita actualizar la edad de un usuario.
- Ejercicio 2: Añade una nueva tabla
Postcon los campostitleycontent, y crea relaciones entreUseryPost.
Soluciones
Solución Ejercicio 1
main :: IO ()
main = do
args <- getArgs
case args of
("add":name:age:_) -> runDb $ insert_ (User name (read age))
("list":_) -> runDb $ selectList [] [] >>= liftIO . print
("delete":name:_) -> runDb $ deleteWhere [UserName ==. name]
("update":name:age:_) -> runDb $ updateWhere [UserName ==. name] [UserAge =. read age]
_ -> putStrLn "Usage: add <name> <age> | list | delete <name> | update <name> <age>"Solución Ejercicio 2
share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
User
name String
age Int
deriving Show
Post
title String
content String
userId UserId
deriving Show
|]Conclusión
En esta sección, hemos aprendido cómo interactuar con bases de datos en Haskell utilizando la biblioteca persistent. Hemos cubierto la configuración del entorno, la definición de esquemas y las operaciones básicas de CRUD. Además, hemos implementado un ejemplo práctico y proporcionado ejercicios para reforzar los conceptos aprendidos.
En el próximo tema, exploraremos cómo realizar pruebas y depuración en Haskell para asegurar la calidad y el correcto funcionamiento de nuestras aplicaciones.
Curso de Programación en Haskell
Módulo 1: Introducción a Haskell
- ¿Qué es Haskell?
- Configuración del Entorno de Haskell
- Sintaxis Básica y Hola Mundo
- Haskell REPL (GHCi)
