packages feed

pg-schema-0.5.0.0: src/PgSchema/Utils/Instances.hs

{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-orphans #-}
module PgSchema.Utils.Instances where

import Data.Kind (Type)
import Data.Singletons
import Data.Text as T
import GHC.TypeLits


-- * Bool singletons (avoid "Data.Bool.Singletons" from singletons-base)

data SBool :: Bool -> Type where
  SFalse :: SBool 'False
  STrue :: SBool 'True

type instance Sing @Bool = SBool

instance SingKind Bool where
  type Demote Bool = Bool
  fromSing SFalse = False
  fromSing STrue = True
  toSing False = SomeSing SFalse
  toSing True = SomeSing STrue

instance SingI 'False where sing = SFalse

instance SingI 'True where sing = STrue

-- * Maybe singletons (avoid "Data.Maybe.Singletons" from singletons-base)

data SMaybe :: Maybe k -> Type where
  SNothing :: SMaybe 'Nothing
  SJust :: Sing x -> SMaybe ('Just x)

type instance Sing @(Maybe k) = SMaybe

instance SingKind k => SingKind (Maybe k) where
  type Demote (Maybe k) = Maybe (Demote k)
  fromSing SNothing = Nothing
  fromSing (SJust sx) = Just (fromSing sx)
  toSing Nothing = SomeSing SNothing
  toSing (Just x) = case toSing x of
    SomeSing sx -> SomeSing (SJust sx)

instance SingI 'Nothing where
  sing = SNothing

instance SingI x => SingI ('Just x) where
  sing = SJust sing

-- * @Symbol@ singletons (@Demote Symbol = Text@, as in @pg-schema@)

type instance Sing @Symbol = SSymbol

instance SingKind Symbol where
  type Demote Symbol = Text
  fromSing (SSymbol :: SSymbol n) = T.pack (symbolVal (Proxy @n))
  toSing t = case someSymbolVal (T.unpack t) of
    SomeSymbol (_ :: Proxy n) -> SomeSing (SSymbol @n)

instance KnownSymbol n => SingI n where
  sing = SSymbol

-- * List singletons

data SList :: [k] -> Type where
  SNil :: SList '[]
  SCons :: Sing x -> Sing xs -> SList (x ': xs)

type instance Sing @[k] = SList

instance SingKind k => SingKind [k] where
  type Demote [k] = [Demote k]
  fromSing SNil = []
  fromSing (SCons sx sxs) = fromSing sx : fromSing sxs
  toSing [] = SomeSing SNil
  toSing (x : xs) = case (toSing x, toSing xs) of
    (SomeSing sx, SomeSing sxs) -> SomeSing (SCons sx sxs)

instance SingI '[] where
  sing = SNil

instance (SingI x, SingI xs) => SingI (x ': xs) where
  sing = SCons sing sing

-- * Pair singletons

data STuple2 :: (a, b) -> Type where
  STuple2 :: Sing x -> Sing y -> STuple2 '(x, y)

type instance Sing @(a, b) = STuple2

instance (SingKind a, SingKind b) => SingKind (a, b) where
  type Demote (a, b) = (Demote a, Demote b)
  fromSing (STuple2 sx sy) = (fromSing sx, fromSing sy)
  toSing (x, y) = case (toSing x, toSing y) of
    (SomeSing sx, SomeSing sy) -> SomeSing (STuple2 sx sy)

instance (SingI a, SingI b) => SingI '(a, b) where
  sing = STuple2 sing sing