packages feed

singletons-th-3.2: src/Data/Singletons/TH/Deriving/Enum.hs

-----------------------------------------------------------------------------
-- |
-- Module      :  Data.Singletons.TH.Deriving.Enum
-- Copyright   :  (C) 2015 Richard Eisenberg
-- License     :  BSD-style (see LICENSE)
-- Maintainer  :  Ryan Scott
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Implements deriving of Enum instances
--
----------------------------------------------------------------------------

module Data.Singletons.TH.Deriving.Enum ( mkEnumInstance ) where

import Language.Haskell.TH.Syntax
import Language.Haskell.TH.Ppr
import Language.Haskell.TH.Desugar
import Data.Singletons.TH.Deriving.Util
import Data.Singletons.TH.Names
import Data.Singletons.TH.Syntax
import Data.Singletons.TH.Util
import Control.Monad
import Data.Maybe

-- monadic for failure only
mkEnumInstance :: DsMonad q => DerivDesc q
mkEnumInstance mb_ctxt ty (DataDecl _ _ _ cons) = do
  -- GHC only allows deriving Enum instances for enumeration types (i.e., those
  -- data types whose constructors all lack fields). We perform the same
  -- validity check here.
  --
  -- GHC actually goes further than we do. GHC will give a specific error
  -- message if you attempt to derive an instance for a "non-vanilla" data
  -- type—that is, a data type that uses features not expressible with
  -- Haskell98 syntax, such as existential quantification. Checking whether
  -- a type variable is existentially quantified is difficult in Template
  -- Haskell, so we omit this check.
  when (null cons ||
        any (\(DCon _ _ _ f _) -> not (null $ tysOfConFields f)) cons) $
    fail ("Can't derive Enum instance for " ++ pprint (typeToTH ty) ++ ".")

  n <- qNewName "n"
  let to_enum = UFunction [DClause [DVarP n] (to_enum_rhs cons [0..])]
      to_enum_rhs [] _ = DVarE errorName `DAppE` DLitE (StringL "toEnum: bad argument")
      to_enum_rhs (DCon _ _ name _ _ : rest) (num:nums) =
        DCaseE (DVarE equalsName `DAppE` DVarE n `DAppE` DLitE (IntegerL num))
          [ DMatch (DConP trueName  [] []) (DConE name)
          , DMatch (DConP falseName [] []) (to_enum_rhs rest nums) ]
      to_enum_rhs _ _ = error "Internal error: exhausted infinite list in to_enum_rhs"

      from_enum = UFunction (zipWith (\i con -> DClause [DConP (extractName con) [] []]
                                                        (DLitE (IntegerL i)))
                                     [0..] cons)
  return (InstDecl { id_cxt     = fromMaybe [] mb_ctxt
                   , id_name    = enumName
                   , id_arg_tys = [ty]
                   , id_sigs    = mempty
                   , id_meths   = [ (toEnumName, to_enum)
                                  , (fromEnumName, from_enum) ] })