packages feed

penelope-core-0.1.0.0: src/Penelope/Core/Send.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

{- |
Module      : Penelope.Core.Send
Description : Zero-overhead smart constructors for first-order and higher-order effect operations
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)

This module provides zero-overhead smart constructors ('sendPolyF', 'sendPolyH')
injecting algebraic effect operations into the 'Penelope' polycategorical core.

See Note [Unpacked Polycategorical Representation] [ref:note-unpacked-representation].
-}
module Penelope.Core.Send
  ( -- * Smart Constructors
    sendPolyF
  , sendPolyF'
  , sendPolyH
  , sendPolyH'
  ) where

import Data.Kind (Type)
import GHC.Exts (Any, Word (W#), unsafeCoerce#)
import GHC.TypeLits (Symbol)
import Penelope.Core.Syntax (Penelope (..), PolyEffect, PolyHEffect)
import Polytropos (Record, RowOf)
import Polytropos.Core (ConstructorIndex (..), Row)
import Polytropos.RowMorph (KnownRowMorph (..))

{- |
Inject a first-order algebraic operation into the syntax tree.
Looks up the constructor tag via 'ConstructorIndex' solved at compile time by @Polytropos.Plugin@,
and wires input ports via 'KnownRowMorph'.

See Note [Unpacked Polycategorical Representation] [ref:note-unpacked-representation].
-}
sendPolyF ::
  forall (l :: Symbol) (opIns :: RowOf Type) (ins :: RowOf Type) (exits :: Row) (eh :: RowOf PolyHEffect) (ef :: RowOf PolyEffect) (outs :: RowOf Type).
  (ConstructorIndex l ef, KnownRowMorph opIns ins) =>
  Any
  -> Record exits
  -> Penelope eh ef ins outs
{-# INLINE sendPolyF #-}
sendPolyF payload conts =
  case constructorIndex @_ @l @ef of
    W# tag# -> OpF tag# (rowMorph @opIns @ins) payload conts

-- | Typed payload variant of 'sendPolyF' applying 'unsafeCoerce#' without runtime boxing.
sendPolyF' ::
  forall (l :: Symbol) (opIns :: RowOf Type) (ins :: RowOf Type) (exits :: Row) op (eh :: RowOf PolyHEffect) (ef :: RowOf PolyEffect) (outs :: RowOf Type).
  (ConstructorIndex l ef, KnownRowMorph opIns ins) =>
  op
  -> Record exits
  -> Penelope eh ef ins outs
{-# INLINE sendPolyF' #-}
sendPolyF' op conts = sendPolyF @l @opIns (unsafeCoerce# op) conts

{- |
Inject a higher-order scoped algebraic operation into the syntax tree with
explicit sub-regions and multi-exit branch continuations.

See Note [Unpacked Polycategorical Representation] [ref:note-unpacked-representation].
-}
sendPolyH ::
  forall (l :: Symbol) (opIns :: RowOf Type) (ins :: RowOf Type) (regions :: Row) (exits :: Row) (eh :: RowOf PolyHEffect) (ef :: RowOf PolyEffect) (outs :: RowOf Type).
  (ConstructorIndex l eh, KnownRowMorph opIns ins) =>
  Record regions
  -> Any
  -> Record exits
  -> Penelope eh ef ins outs
{-# INLINE sendPolyH #-}
sendPolyH regions payload conts =
  case constructorIndex @_ @l @eh of
    W# tag# -> OpH tag# (rowMorph @opIns @ins) regions payload conts

-- | Typed payload variant of 'sendPolyH' applying 'unsafeCoerce#' without runtime boxing.
sendPolyH' ::
  forall (l :: Symbol) (opIns :: RowOf Type) (ins :: RowOf Type) (regions :: Row) (exits :: Row) op (eh :: RowOf PolyHEffect) (ef :: RowOf PolyEffect) (outs :: RowOf Type).
  (ConstructorIndex l eh, KnownRowMorph opIns ins) =>
  Record regions
  -> op
  -> Record exits
  -> Penelope eh ef ins outs
{-# INLINE sendPolyH' #-}
sendPolyH' regions op conts = sendPolyH @l @opIns regions (unsafeCoerce# op) conts