packages feed

fir-0.1.0.0: src/Data/Type/Known.hs

{-# LANGUAGE AllowAmbiguousTypes    #-}
{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE MultiParamTypeClasses  #-}
{-# LANGUAGE PolyKinds              #-}
{-# LANGUAGE ScopedTypeVariables    #-}
{-# LANGUAGE TypeApplications       #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE UndecidableInstances   #-}

{-|
Module: Data.Type.Known

This module provides a simple type class for reification (turning type-level data into value-level data).

This allows programs to perform type-level computations (e.g. to perform validation at compile time),
and at the end turn this type-level data back into value-level data to be used e.g. by the code generator.

This is a one way process: these are not singletons. See "FIR.Prim.Types" for the singletons this library uses internally.

Note that some instances are tailored to this library: type-level literals 'Symbol' are turned into 'ShortText',
and type-level natural numbers 'Nat' are turned into 'Word32'.
-}


module Data.Type.Known where

-- base
import Data.Kind
  ( Type )
import Data.Proxy
  ( Proxy(Proxy) )
import Data.Word
  ( Word32 )
import GHC.TypeLits
  ( Symbol, KnownSymbol, symbolVal )
import GHC.TypeNats
  ( Nat, KnownNat, natVal )

-- text-short
import Data.Text.Short
  ( ShortText )
import qualified Data.Text.Short as ShortText

-- fir
import Data.Type.Map
  ( (:->)((:->)) )

--------------------------------------------------

class Demotable k where
  type Demote k = (d :: Type) | d -> k

class Demotable k => Known (k :: Type) (a :: k) where
  known :: Demote k

-- make the kind parameter invisible
type KindOf (a :: k) = k
knownValue :: forall a. Known (KindOf a) a => Demote (KindOf a)
knownValue = known @(KindOf a) @a

instance Demotable Symbol where
  type Demote Symbol = ShortText
instance KnownSymbol k => Known Symbol k where
  known = ShortText.pack (symbolVal @k Proxy)

instance Demotable Nat where
  type Demote Nat = Word32
instance KnownNat n => Known Nat n where
  known = fromIntegral (natVal @n Proxy)

instance Demotable Bool where
  type Demote Bool = Bool

instance Known Bool 'False where
  known = False
instance Known Bool 'True where
  known = True

instance Demotable k => Demotable (Maybe k) where
  type Demote (Maybe k) = Maybe (Demote k)
instance Demotable k => Known (Maybe k) 'Nothing where
  known = Nothing
instance Known k a => Known (Maybe k) ('Just a) where
  known = Just ( known @k @a )

instance Demotable () where
  type Demote () = ()

instance Known () '() where
  known = ()

instance (Demotable k, Demotable l) => Demotable (k,l) where
  type Demote (k,l) = (Demote k, Demote l)
instance (Known k a, Known l b) => Known (k,l) '(a,b) where
  known = ( known @k @a, known @l @b )

instance (Demotable k, Demotable l, Demotable m) => Demotable (k,l,m) where
  type Demote (k,l,m) = (Demote k, Demote l, Demote m)
instance (Known k a, Known l b, Known m c) => Known (k,l,m) '(a,b,c) where
  known = ( known @k @a, known @l @b, known @m @c )

instance Demotable k => Demotable [k] where
  type Demote [k] = [Demote k]
instance Demotable k => Known [k] '[] where
  known = []
instance ( Known k a, Known [k] as ) => Known [k] (a ': as) where
  known = known @k @a : known @[k] @as

instance ( Demotable k, Demotable v ) => Demotable (k :-> v) where
  type Demote (k :-> v) = Demote k :-> Demote v
instance ( Known k a, Known v b ) => Known (k :-> v) (a ':-> b) where
  known = known @k @a :-> known @v @b