packages feed

fir-0.1.0.0: src/FIR/AST/Type.hs

{-# LANGUAGE DataKinds              #-}
{-# LANGUAGE FlexibleContexts       #-}
{-# LANGUAGE FlexibleInstances      #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE PolyKinds              #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators          #-}
{-# LANGUAGE UndecidableInstances   #-}

{-|
Module: FIR.AST.Type

Augmented annotation type for the AST GADT.

Distinguishes between a partially applied constructor of the AST
and a fully applied constructor of the AST that returns a function.

Enforces a distinction between pure and stateful operations,
as necessary to be able to write an evaluator.
-}

module FIR.AST.Type
  ( AugType(..), Eff, UnderlyingType
  , FunArgs, FunRes, FunTys, MkFunType, ApplyFun
  , Nullary, MapVal
  , ApplyFAug, MapApplyFAug
  )
  where

-- base
import Data.Kind
  ( Type )

-- fir
import Control.Monad.Indexed
  ( (:=) )
import {-# SOURCE #-} FIR.ProgramState
  ( ProgramState )

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

infixr 0 :-->
-- | Types of values the AST can hold: concrete values (pure or effectful), and functions.
data AugType
  = Val Type
  | MkEff Type ProgramState ProgramState -- last parameter must be starting indexed monadic state (McBride formalism)
  | AugType :--> AugType

type Eff i j a = MkEff a j i

-- | Return the underlying type of an augmented type.
type family UnderlyingType ( t :: AugType ) :: Type where
  UnderlyingType (Val ty) = ty
  UnderlyingType (Eff i j a) = (a := j) i
  UnderlyingType (s :--> t) = UnderlyingType s -> UnderlyingType t

class    ( as ~ FunArgs t, b ~ FunRes t, t ~ MkFunType as b, Nullary b )
      => FunTys (t :: AugType) (as :: [AugType]) (b :: AugType) | t -> as b, as b -> t
instance ( as ~ FunArgs t, b ~ FunRes t, t ~ MkFunType as b, Nullary b )
      => FunTys t as b

-- | Returns list of arguments of an augmented type.
type family FunArgs (t :: AugType) :: [AugType] where
  FunArgs (Val _) = '[]
  FunArgs (Eff _ _ _) = '[]
  FunArgs (a :--> b) = a ': FunArgs b

type family FunRes (t :: AugType) :: AugType where
  FunRes (a :--> b) = FunRes b
  FunRes b          = b
type family MkFunType (as :: [AugType]) (b :: AugType) :: AugType where
  MkFunType '[]       b = b
  MkFunType (a ': as) b = a :--> MkFunType as b

class    ( FunArgs a ~ '[], FunRes a ~ a ) => Nullary a
instance ( FunArgs a ~ '[], FunRes a ~ a ) => Nullary a

type family ApplyFun (a :: AugType) (as :: [AugType]) :: AugType where
  ApplyFun a '[] = a
  ApplyFun (a :--> b) (a ': as) = ApplyFun b as

-- | Augment a list of types of values.
type family MapVal (ts :: [Type]) = (as :: [AugType]) | as -> ts where
  MapVal '[]       = '[]
  MapVal (t ': ts) = Val t ': MapVal ts

------------------------------------------------
-- Dealing with applying/unapplying functors.

-- | Apply a functor to an augmented type.
type family ApplyFAug (f :: Type -> Type) (t :: AugType) = ( r :: AugType ) | r -> f t where
  ApplyFAug f (Val a    ) = Val (f a)
  ApplyFAug f (a :--> b ) = ApplyFAug f a :--> ApplyFAug f b
  ApplyFAug f (Eff i j a) = Eff i j (f a)

type family MapApplyFAug (f :: Type -> Type) (fas :: [AugType]) :: [AugType] where
  MapApplyFAug _ '[] = '[]
  MapApplyFAug f ( Val a ': as ) = Val (f a) ': MapApplyFAug f as