How to do it...

  1. Create a new project called custom-datatype with the simple stack template:
        stack new custom-datatype simple
  1. Add dependencies on the persistentpersistent-template, persistent-sqlitetext, 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
  1. Open src/Main.hs. We will be adding our source here.

  2. 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              #-}
  1. Add the declaration for the Main module:
 module Main where
  1. 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
  1. 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
  1. 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"
  1. Define the custom field for the email address:
 derivePersistField "EmailAddress"
  1. Close the file, and return to src/Main.hs. Write the model definition:
 share [mkPersist sqlSettings, mkMigrate "migrateAll"]  
[persistLowerCase| User status Status email EmailAddress |]
  1. 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"
  1. Build and execute the project:
 stack build
 stack exec -- custom-datatype
  1. You should see the following output: