En aquest tema, aplicarem tots els conceptes apresos al llarg del curs per construir una aplicació completa en Haskell. Aquest projecte servirà com a culminació del teu aprenentatge i et proporcionarà una experiència pràctica en el desenvolupament d'aplicacions reals amb Haskell.
Objectius del Projecte
- Aplicar els conceptes de programació funcional en un projecte real.
- Utilitzar el sistema de tipus de Haskell per garantir la seguretat i la correcció del codi.
- Implementar funcionalitats d'entrada i sortida (I/O).
- Gestionar errors i excepcions de manera robusta.
- Integrar amb bases de dades i altres serveis externs.
- Escriure proves per assegurar la qualitat del codi.
Descripció del Projecte
Construirem una aplicació de gestió de tasques (To-Do List) que permetrà als usuaris crear, llegir, actualitzar i eliminar tasques. L'aplicació tindrà una interfície de línia de comandes (CLI) i utilitzarà una base de dades SQLite per emmagatzemar les dades.
Requisits
- Haskell (GHC)
- Cabal o Stack (per gestionar les dependències)
- SQLite (per a la base de dades)
Estructura del Projecte
El projecte es dividirà en diversos mòduls per mantenir el codi organitzat i modular:
- Main.hs: Punt d'entrada de l'aplicació.
- Database.hs: Gestió de la base de dades.
- Task.hs: Definició del tipus de dades
Taski funcions associades. - CLI.hs: Interfície de línia de comandes.
Pas 1: Configuració del Projecte
Primer, crea un nou projecte Haskell utilitzant Cabal o Stack.
Utilitzant Cabal
Utilitzant Stack
Pas 2: Definició del Tipus de Dades Task
Creem un nou fitxer src/Task.hs per definir el tipus de dades Task i les funcions associades.
module Task where
data Task = Task
{ taskId :: Int
, taskDescription :: String
, taskCompleted :: Bool
} deriving (Show, Eq)
-- Funció per crear una nova tasca
createTask :: Int -> String -> Task
createTask id desc = Task id desc False
-- Funció per marcar una tasca com a completada
completeTask :: Task -> Task
completeTask task = task { taskCompleted = True }Pas 3: Gestió de la Base de Dades
Creem un nou fitxer src/Database.hs per gestionar la base de dades SQLite.
module Database where import Database.SQLite.Simple import Task -- Funció per inicialitzar la base de dades initDB :: IO () initDB = do conn <- open "tasks.db" execute_ conn "CREATE TABLE IF NOT EXISTS tasks (id INTEGER PRIMARY KEY, description TEXT, completed BOOLEAN)" close conn -- Funció per afegir una tasca a la base de dades addTask :: Task -> IO () addTask task = do conn <- open "tasks.db" execute conn "INSERT INTO tasks (id, description, completed) VALUES (?, ?, ?)" (taskId task, taskDescription task, taskCompleted task) close conn -- Funció per obtenir totes les tasques getTasks :: IO [Task] getTasks = do conn <- open "tasks.db" tasks <- query_ conn "SELECT id, description, completed FROM tasks" :: IO [(Int, String, Bool)] close conn return $ map (\(id, desc, comp) -> Task id desc comp) tasks
Pas 4: Interfície de Línia de Comandes (CLI)
Creem un nou fitxer src/CLI.hs per gestionar la interfície de línia de comandes.
module CLI where
import System.Environment (getArgs)
import Task
import Database
-- Funció principal de la CLI
mainCLI :: IO ()
mainCLI = do
args <- getArgs
case args of
["init"] -> initDB
["add", desc] -> do
tasks <- getTasks
let newId = if null tasks then 1 else taskId (last tasks) + 1
let newTask = createTask newId desc
addTask(newTask)
putStrLn "Task added!"
["list"] -> do
tasks <- getTasks
mapM_ print tasks
_ -> putStrLn "Unknown command"Pas 5: Punt d'Entrada de l'Aplicació
Finalment, creem el fitxer app/Main.hs com a punt d'entrada de l'aplicació.
Pas 6: Proves i Depuració
És important escriure proves per assegurar la qualitat del codi. Creem un fitxer test/Spec.hs per a les proves.
module Main where
import Test.Hspec
import Task
main :: IO ()
main = hspec $ do
describe "Task" $ do
it "creates a new task" $ do
let task = createTask 1 "Test task"
taskDescription task `shouldBe` "Test task"
taskCompleted task `shouldBe` False
it "completes a task" $ do
let task = createTask 1 "Test task"
let completedTask = completeTask task
taskCompleted completedTask `shouldBe` TrueConclusió
En aquest projecte, hem construït una aplicació de gestió de tasques utilitzant Haskell. Hem aplicat conceptes de programació funcional, gestió de bases de dades, i hem creat una interfície de línia de comandes. Aquest projecte t'hauria de proporcionar una bona base per desenvolupar aplicacions més complexes en Haskell.
Exercicis Pràctics
- Afegir Funcionalitat de Supressió de Tasques: Implementa una funció per eliminar tasques de la base de dades.
- Marcar Tasques com a Completes: Implementa una funció per marcar tasques com a completes des de la CLI.
- Millorar la Interfície de Línia de Comandes: Afegeix més opcions i millora la usabilitat de la CLI.
Solucions als Exercicis
- Afegir Funcionalitat de Supressió de Tasques
-- Afegeix aquesta funció a Database.hs
deleteTask :: Int -> IO ()
deleteTask id = do
conn <- open "tasks.db"
execute conn "DELETE FROM tasks WHERE id = ?" (Only id)
close conn
-- Afegeix aquesta opció a CLI.hs
case args of
["delete", idStr] -> do
let id = read idStr :: Int
deleteTask id
putStrLn "Task deleted!"
- Marcar Tasques com a Completes
-- Afegeix aquesta funció a Database.hs
completeTaskInDB :: Int -> IO ()
completeTaskInDB id = do
conn <- open "tasks.db"
execute conn "UPDATE tasks SET completed = 1 WHERE id = ?" (Only id)
close conn
-- Afegeix aquesta opció a CLI.hs
case args of
["complete", idStr] -> do
let id = read idStr :: Int
completeTaskInDB id
putStrLn "Task completed!"
- Millorar la Interfície de Línia de Comandes
-- Afegeix més opcions i millora la usabilitat de la CLI
case args of
["init"] -> initDB
["add", desc] -> do
tasks <- getTasks
let newId = if null tasks then 1 else taskId (last tasks) + 1
let newTask = createTask newId desc
addTask(newTask)
putStrLn "Task added!"
["list"] -> do
tasks <- getTasks
mapM_ print tasks
["delete", idStr] -> do
let id = read idStr :: Int
deleteTask id
putStrLn "Task deleted!"
["complete", idStr] -> do
let id = read idStr :: Int
completeTaskInDB id
putStrLn "Task completed!"
_ -> putStrLn "Unknown command. Available commands: init, add, list, delete, complete"Amb aquests exercicis i solucions, hauràs millorat la funcionalitat de la teva aplicació i hauràs adquirit més experiència en el desenvolupament amb Haskell.
