packages feed

casa-reoc-0.1.0.0: src/ReOC/Data/CourseType.hs

{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wall #-}
-- The checks of inheritance (inheritCourseType) have a constraint that their body does not use, by design.
{-# OPTIONS_GHC -Wno-redundant-constraints #-}

-- | The data type @CourseType@: a type of RePL training course (CASA Form 101-02, question 29).
module ReOC.Data.CourseType (
  CourseType (..),
  CourseTypeLike (..),
  lawNamesCourseType,
  InheritCourseType,
  inheritCourseType,
) where

import Control.Lens (Choice, Optic, prism', review)
import Data.Functor.Const (Const)
import ReOC.Name.InitialCourse (InitialCourseLike (..))
import ReOC.Name.UpgradeDifferentCategory (UpgradeDifferentCategoryLike (..))
import ReOC.Name.UpgradeSameCategory (UpgradeSameCategoryLike (..))

-- | A type of RePL training course (CASA Form 101-02, question 29).
data CourseType
  = InitialCourse
  | UpgradeSameCategory
  | UpgradeDifferentCategory
  deriving (Eq, Ord, Show)

-- | The name of the data type @CourseType@.
class CourseTypeLike p f x x' where
  courseType ::
    Optic p f x x' CourseType CourseType

-- | Identity
instance (x' ~ CourseType) => CourseTypeLike p f CourseType x' where
  courseType = id

-- | Prism
instance (Choice p, Applicative f, x' ~ CourseType) => InitialCourseLike p f CourseType x' where
  initialCourse = prism' (\() -> InitialCourse) (\case InitialCourse -> Just (); UpgradeSameCategory -> Nothing; UpgradeDifferentCategory -> Nothing)

-- | Prism
instance (Choice p, Applicative f, x' ~ CourseType) => UpgradeSameCategoryLike p f CourseType x' where
  upgradeSameCategory = prism' (\() -> UpgradeSameCategory) (\case InitialCourse -> Nothing; UpgradeSameCategory -> Just (); UpgradeDifferentCategory -> Nothing)

-- | Prism
instance (Choice p, Applicative f, x' ~ CourseType) => UpgradeDifferentCategoryLike p f CourseType x' where
  upgradeDifferentCategory = prism' (\() -> UpgradeDifferentCategory) (\case InitialCourse -> Nothing; UpgradeSameCategory -> Nothing; UpgradeDifferentCategory -> Just ())

{- | Rebuilds a value from the name of each field or constructor.

Laws:

> lawNamesCourseType == id
-}
lawNamesCourseType :: CourseType -> CourseType
lawNamesCourseType = \case
  InitialCourse -> review initialCourse ()
  UpgradeSameCategory -> review upgradeSameCategory ()
  UpgradeDifferentCategory -> review upgradeDifferentCategory ()

-- | The classes that a data type holding @CourseType@ in one field, or in one or more constructors, inherits.
type InheritCourseType s =
  ( CourseTypeLike (->) (Const ()) s s
  , InitialCourseLike (->) (Const ()) s s
  , UpgradeSameCategoryLike (->) (Const ()) s s
  , UpgradeDifferentCategoryLike (->) (Const ()) s s
  )

-- | Checks that a data type inherits every class of @InheritCourseType@.
inheritCourseType :: (InheritCourseType s) => proxy s -> ()
inheritCourseType _ = ()