packages feed

fir-0.1.0.0: src/FIR/Generics.hs

{-# LANGUAGE AllowAmbiguousTypes      #-}
{-# LANGUAGE DataKinds                #-}
{-# LANGUAGE DeriveAnyClass           #-}
{-# LANGUAGE DeriveGeneric            #-}
{-# LANGUAGE DerivingVia              #-}
{-# LANGUAGE FlexibleContexts         #-}
{-# LANGUAGE FlexibleInstances        #-}
{-# LANGUAGE GADTs                    #-}
{-# LANGUAGE InstanceSigs             #-}
{-# LANGUAGE MultiParamTypeClasses    #-}
{-# LANGUAGE PatternSynonyms          #-}
{-# LANGUAGE PolyKinds                #-}
{-# LANGUAGE ScopedTypeVariables      #-}
{-# LANGUAGE StandaloneDeriving       #-}
{-# LANGUAGE StandaloneKindSignatures #-}
{-# LANGUAGE TypeApplications         #-}
{-# LANGUAGE TypeFamilies             #-}
{-# LANGUAGE TypeOperators            #-}
{-# LANGUAGE UndecidableInstances     #-}
module FIR.Generics (FromGenericProduct(..)) where

-- base
import Prelude
  hiding ( undefined )
import Data.Typeable
import Data.Kind
  ( Type )
import GHC.TypeLits ( Symbol, KnownSymbol )

-- variant
import Data.Variant.EGADT
  ( pattern VF )

-- fir
import Data.Type.Map

import FIR.AST.Prim
import FIR.AST.Type

import FIR.Prim.Struct
import FIR.Prim.Types
  ( PrimTyMap )

import FIR.AST

-- generics-sop
import Generics.SOP (NP(..), I(..), K(..))
import qualified Generics.SOP as SOP

------------------------------------------------
-- Generic deriving for Syntactic

-- | Newtype to automatically derive Syntactic for instances generically for product types.
--
-- Example
--
-- @
-- data MinMax = MinMax Float Float
--   deriving (GHC.Generic, SOP.Generic)
--   deriving Syntactic via (FromGenericProduct MinMax ["min", "max"])
-- @
type FromGenericProduct :: Type -> [Symbol] -> Type
newtype FromGenericProduct t xs = FromGenericProduct t

instance forall (t :: Type) (ts :: [Type]) (xs :: [Symbol]) (zgs :: [Symbol :-> Type]) (zgs' :: [Symbol :-> Type]).
         ( SOP.IsProductType t ts

         , zgs  ~ ZipGenericSyntactic  xs ts
         , zgs' ~ ZipGenericSyntactic' xs ts

         , PrimTyMap zgs

         , ASTStructFields AST zgs' zgs
         , StructFResultFromArgs zgs' ~ zgs
         , StructFArgsFromResult zgs AST ~ zgs'

         , SOP.AllZip SyntacticRel xs ts
         , SOP.All Typeable ts
         , SOP.All KnownSymbol xs
         ) => Syntactic (FromGenericProduct t xs) where
  type Internal (FromGenericProduct t xs) = Val (Struct (ZipGenericSyntactic xs (SOP.ProductCode t)))

  toAST :: FromGenericProduct t xs -> AST (Val (Struct (ZipGenericSyntactic xs ts)))
  toAST (FromGenericProduct a)
    = Struct (npToStruct (mkNP @xs) (SOP.productTypeFrom a))
    where
      npToStruct :: forall ns ys
                  . (SOP.AllZip SyntacticRel ns ys)
                 => NP (K ()) ns
                 -> NP I ys
                 -> Struct (ZipGenericSyntactic' ns ys)
      npToStruct Nil   Nil = End
      npToStruct (_ SOP.:* xs) (SOP.I y SOP.:* ys) = toAST y :& npToStruct xs ys

  fromAST :: AST (Val (Struct (ZipGenericSyntactic xs ts))) -> FromGenericProduct t xs
  fromAST (VF (StructF struct)) = FromGenericProduct $
    SOP.productTypeTo @t (structToNp (mkNP @xs) struct)
    where
      structToNp :: forall (ns :: [Symbol]) ys
                  . ( SOP.AllZip SyntacticRel ns ys )
                 => NP (K ()) ns
                 -> Struct (ZipGenericSyntactic' ns ys)
                 -> NP I ys
      structToNp Nil End = Nil
      structToNp ((_ :: (K () (n :: Symbol))) :* xs) (y :& ys)
        = I (fromAST y) :* structToNp xs ys
  fromAST _ = error "impossible"

mkNP :: forall ns. SOP.SListI ns => NP (K ()) ns
mkNP = case SOP.sList @ns of
  SOP.SNil -> Nil
  SOP.SCons -> K () SOP.:* mkNP

type family ZipGenericSyntactic (xs :: [Symbol]) (ys :: [Type]) where
  ZipGenericSyntactic '[] '[] = '[]
  ZipGenericSyntactic (x : xs) (y : ys) = (x ':-> InternalType y) : ZipGenericSyntactic xs ys

type family ZipGenericSyntactic' (xs :: [Symbol]) (ys :: [Type]) where
  ZipGenericSyntactic' '[] '[] = '[]
  ZipGenericSyntactic' (x : xs) (y : ys) = (x ':-> AST (Val y)) : ZipGenericSyntactic' xs ys

class (Val y ~ Internal y, Syntactic y) => SyntacticRel x y
instance (Val y ~ Internal y, Syntactic y) => SyntacticRel x y