packages feed

moonlight-triangulation-0.1.0.0: src-build/Moonlight/Triangulation/Internal/Cdt/Combinators.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}

-- | Bounded monadic folding, refusable transaction sequencing, and the handle
-- and error coercions the constrained layer shares.
module Moonlight.Triangulation.Internal.Cdt.Combinators
  ( foldWhileM
  , bindMutable
  , asConstraintStep
  , mapLeft
  , vertexInt
  , directedInt
  ) where

import Moonlight.Triangulation.Handles.HandleDefs
  ( DirectedEdgeId (..)
  , VertexId (..)
  )
import Moonlight.Triangulation.Internal.Cdt.Types (CdtError (..))
import Moonlight.Triangulation.Internal.Types (BuildError)

-- | Monadic left fold whose continuation is supplied by a lazy right fold.
-- The state predicate decides descent before the next effect is constructed,
-- so graph walks stop at their authoritative local answer without mutable loop
-- control or traversing the unused safety suffix.
foldWhileM
  :: (Foldable container, Monad monad)
  => (state -> Bool)
  -> (state -> item -> monad state)
  -> state
  -> container item
  -> monad state
foldWhileM shouldContinue step initial items =
  foldr
    (\item continuation state ->
      if shouldContinue state
        then step state item >>= continuation
        else pure state
    )
    pure
    items
    initial
{-# INLINE foldWhileM #-}

-- | Sequence two refusable transaction steps. Refusal short-circuits, so a
-- transaction that abandons never reaches its publication; writing the bind
-- once is what keeps the constraint verbs from nesting their case analysis
-- five deep.
bindMutable :: Monad monad => monad (Either failure a) -> (a -> monad (Either failure b)) -> monad (Either failure b)
bindMutable step continue = do
  outcome <- step
  case outcome of
    Left failure -> pure (Left failure)
    Right value -> continue value
{-# INLINE bindMutable #-}

-- | Relabel a step whose refusal is a build failure, so it composes with the
-- constraint layer's own.
asConstraintStep :: Functor f => f (Either BuildError a) -> f (Either (CdtError) a)
asConstraintStep = fmap (mapLeft CdtBuildError)
{-# INLINE asConstraintStep #-}

mapLeft :: (left -> right) -> Either left value -> Either right value
mapLeft convert = either (Left . convert) Right

vertexInt :: VertexId -> Int
vertexInt (VertexId value) = fromIntegral value

directedInt :: DirectedEdgeId -> Int
directedInt (DirectedEdgeId value) = fromIntegral value