packages feed

hypertypes-0.2.2: src/Hyper/Unify/Occurs.hs

-- | Occurs check (check whether unification terms recursively contains themselves)
module Hyper.Unify.Occurs
    ( occursCheck
    ) where

import Control.Monad (unless, when)
import Control.Monad.Trans.Class (MonadTrans (..))
import Control.Monad.Trans.State (execStateT, get, put)
import Hyper
import Hyper.Class.Unify (BindingDict (..), UVarOf, Unify (..), occursError, semiPruneLookup)
import Hyper.Unify.Term (UTerm (..), uBody)

import Hyper.Internal.Prelude

-- | Occurs check
{-# INLINE occursCheck #-}
occursCheck ::
    forall m t.
    Unify m t =>
    UVarOf m # t ->
    m ()
occursCheck v0 =
    do
        (v1, x) <- semiPruneLookup v0
        case x of
            UResolving t -> occursError v1 t
            UResolved{} -> pure ()
            UUnbound{} -> pure ()
            USkolem{} -> pure ()
            UTerm b ->
                htraverse_
                    ( Proxy @(Unify m) #>
                        \c ->
                            do
                                get >>= lift . (`unless` bindVar binding v1 (UResolving b))
                                put True
                                occursCheck c & lift
                    )
                    (b ^. uBody)
                    & (`execStateT` False)
                    >>= (`when` bindVar binding v1 (UTerm b))
                    \\ unifyRecursive (Proxy @m) (Proxy @t)
            UToVar{} -> error "lookup not expected to result in var (in occursCheck)"
            UConverted{} -> error "conversion state not expected in occursCheck"
            UInstantiated{} -> error "occursCheck during instantiation"