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:

  1. Main.hs: Punt d'entrada de l'aplicació.
  2. Database.hs: Gestió de la base de dades.
  3. Task.hs: Definició del tipus de dades Task i funcions associades.
  4. 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

cabal init

Utilitzant Stack

stack new todo-app
cd todo-app

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ó.

module Main where

import CLI (mainCLI)

main :: IO ()
main = mainCLI

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` True

Conclusió

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

  1. Afegir Funcionalitat de Supressió de Tasques: Implementa una funció per eliminar tasques de la base de dades.
  2. Marcar Tasques com a Completes: Implementa una funció per marcar tasques com a completes des de la CLI.
  3. Millorar la Interfície de Línia de Comandes: Afegeix més opcions i millora la usabilitat de la CLI.

Solucions als Exercicis

  1. 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!"

  1. 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!"

  1. 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.

© Copyright 2024. Tots els drets reservats