- Create a new project called custom-datatype with the simple stack template:
stack new custom-datatype simple
- Add dependencies on the persistent, persistent-template, persistent-sqlite, text, and mtl libraries in the build-depends sub-section of the executable section. Also add email-validate as a dependency. We will use it to store grammatically valid email addresses. Also add a Custom module to the other-modules subsection in the same section (you will have to add this subsection). Other-modules represents a set of modules that are part of the compilation but aren't exposed to the user. We will be adding the Custom module for defining a custom data type:
executable custom-datatype hs-source-dirs: src main-is: Main.hs other-modules: Custom default-language: Haskell2010 build-depends: base >= 4.7 && < 5 , persistent , persistent-template , persistent-sqlite , text , mtl , email-validate
-
Open src/Main.hs. We will be adding our source here.
- Add the extensions required for invoking persistent and persistent-template functions:
{-# LANGUAGE EmptyDataDecls #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE DeriveGeneric #-}
- Add the declaration for the Main module:
module Main where
- Add the required imports:
import Database.Persist.TH import Data.Text as T import Database.Persist.Sqlite import Database.Persist.Sql as S import Control.Monad.Reader import Control.Monad.IO.Class import Text.Email.Validate import Custom
- Open a new file in the same directory called Custom.hs. Add the customary extensions, module declarations, and so on to the file. Note that the custom file must be defined in a separate module, or the module definition produces an error:
{-# LANGUAGE TemplateHaskell #-} module Custom where import Database.Persist.TH import Text.Email.Validate
- Add the user statuses, Active and Inactive. Use derivePersistField to create a custom data type:
data Status = Active | Inactive deriving (Show, Eq, Read) derivePersistField "Status"
- Define the custom field for the email address:
derivePersistField "EmailAddress"
- Close the file, and return to src/Main.hs. Write the model definition:
share [mkPersist sqlSettings, mkMigrate "migrateAll"]
[persistLowerCase| User status Status email EmailAddress |]
- Add the data, and query it:
sampleData :: MonadIO m => ReaderT SqlBackend m () sampleData = do let Right jupitermail = validate "jupyter@planets.com" Right plutomail = validate "pluto@planets.com" Right earthmail = validate "earth@planets.com" insert $ User Custom.Active jupitermail insert $ User Custom.Active earthmail insert $ User Custom.Inactive plutomail return ()
main :: IO () main = runSqlite ":memory:" $ do runMigration migrateAll sampleData -- Get all users (provide empty filter for SQL *) all <- S.count ([] :: [Filter User]) active <- S.count ([UserStatus ==. Custom.Active]) liftIO $ putStrLn $ "There are " ++ show all ++ " users" liftIO $ putStrLn $ show active ++ " are active"
- Build and execute the project:
stack build stack exec -- custom-datatype
- You should see the following output:
