packages feed

penelope-core-0.1.0.0: src/Penelope/Core/Engine/Elaborate.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}

{- |
Module      : Penelope.Core.Engine.Elaborate
Description : Phase 1 single-pass bottom-up higher-order effect elaboration
Copyright   : (c) 2026 The Penelope Authors
License     : EUPL-1.2 OR Apache-2.0
Maintainer  : nadia.yvette.chambers@ik.me
Stability   : experimental
Portability : non-portable (GHC extensions)

Provides Phase 1 single-pass bottom-up macro elaboration of higher-order scoped
effects into pure first-order operations.

See Note [Two-Phase Weaving and Unweaving] [ref:note-two-phase-engine] and
Note [Institutional Satisfaction and Zero-Traversal Weakening] [ref:note-satisfaction-weakening].
-}
module Penelope.Core.Engine.Elaborate
  ( -- * Elaborator Abstraction
    Elaborator (..)
  , Elaborators
  , MapRow

    -- * Phase 1 Elaboration Engine
  , elaborateAll
  ) where

import Data.Functor.Identity (Identity (..))
import Data.Kind (Type)
import Data.Primitive.SmallArray (indexSmallArray)
import GHC.Exts (Any, Int (..), unsafeCoerce#, word2Int#)
import Penelope.Core.Syntax
  ( BranchCont (..)
  , Penelope (..)
  , PolyEffect
  , PolyHEffect
  , Region (..)
  )
import Penelope.Core.Wiring (rmapFused)
import Polytropos (Pair (..), Record, RowOf, gatherRecord)
import Polytropos.Core (Rep (..), RowRep (..))
import Polytropos.RowMorph (RowMorph, unsafeMkRowMorph)

-- | Map a higher-order type constructor over each field of a row.
type family MapRow (f :: k -> Type) (r :: RowOf k) :: RowOf Type where
  MapRow f '[] = '[]
  MapRow f ((l ':= v) ': rest) = (l ':= f v) ': MapRow f rest

-- | Convenient type alias for a record of elaborators.
type Elaborators ef eh = Record (MapRow (Elaborator '[] ef) eh)

{- |
An 'Elaborator' translates a higher-order \((m, n, k, r)\)-ary scoped operation @h@
into a pure first-order 'Penelope' computation.

Notice that:
1. All \(r\) nested regions in @Record regions@ have ALREADY
   been recursively elaborated before 'runElaborator' is called (bottom-up catamorphism).
2. The elaborator receives the \(k\) downstream continuation branches
   @Record exits@ directly, allowing it to wire region exits straight into downstream
   branches without intermediate ADT allocations.

See Note [Two-Phase Weaving and Unweaving] [ref:note-two-phase-engine].
-}
newtype Elaborator
          (eh' :: RowOf PolyHEffect)
          (ef  :: RowOf PolyEffect)
          (h   :: PolyHEffect)
  = Elaborator
  { runElaborator
      :: forall (regions :: RowOf Type) (opIns :: RowOf Type) (exits :: RowOf Type)
                (frameIns :: RowOf Type) (frameOuts :: RowOf Type).
         RowMorph opIns frameIns
      -> Record regions
      -> Any
      -> Record exits
      -> Penelope eh' ef frameIns frameOuts
  }

{- |
Elaborates all higher-order scoped effects @eh@ into pure first-order effects @ef@
in a single bottom-up pass over the syntax tree.
Dispatch at each 'OpH' node is a branchless \(O(1)\) 'indexSmallArray#' read into the
Polytropos 'Record' of elaborators.

Contravariant reduct gathering via 'gatherRecord' adapts outer elaborators to inner
scopes across 'ScopeRow' boundaries in \(O(|eh_{sub}|)\) time without walking the inner sub-tree.

See Note [Two-Phase Weaving and Unweaving] [ref:note-two-phase-engine] and
Note [Institutional Satisfaction and Zero-Traversal Weakening] [ref:note-satisfaction-weakening].
-}
{-# INLINE elaborateAll #-}
elaborateAll ::
  forall (eh :: RowOf PolyHEffect) (ef :: RowOf PolyEffect)
         (ins :: RowOf Type) (outs :: RowOf Type).
  Record (MapRow (Elaborator '[] ef) eh)
  -> Penelope eh ef ins outs
  -> Penelope '[] ef ins outs
elaborateAll !elabs = go
  where
    elabCont ::
      BranchCont (Penelope eh ef i o) branchOuts
      -> BranchCont (Penelope '[] ef i o) branchOuts
    elabCont (BranchCont k) = BranchCont (\branchFrame -> go (k branchFrame))

    elabRegion :: Region eh ef sig -> Region '[] ef sig
    elabRegion (Region subPipe) = Region (elaborateAll elabs subPipe)

    go :: Penelope eh ef i o -> Penelope '[] ef i o
    go = \case
      Route morph ->
        Route morph

      LiftPure inMap f cont ->
        LiftPure inMap f (elabCont cont)

      OpF tag# inMap opAny conts ->
        OpF tag# inMap opAny (rmapFused elabCont conts)

      OpH tag# inMap regions opAny conts ->
        let !elaboratedRegions = rmapFused elabRegion regions
            !elaboratedConts   = rmapFused elabCont conts
            !(RowRep (Rep _ arr)) = elabs
            !(Identity elabAny) = indexSmallArray arr (I# (word2Int# tag#))
            !(Elaborator elabFn) = unsafeCoerce# elabAny
        in elabFn (unsafeCoerce# inMap) elaboratedRegions opAny elaboratedConts

      ScopeRow ehMorph efMorph subPipe ->
        let !subElabs = unsafeCoerce# (gatherRecord (unsafeCoerce# ehMorph) elabs)
        in ScopeRow (unsafeMkRowMorph []) efMorph (elaborateAll subElabs subPipe)