packages feed

morley-1.19.0: src/Morley/Util/Constrained.hs

-- SPDX-FileCopyrightText: 2022 Oxhead Alpha
-- SPDX-License-Identifier: LicenseRef-MIT-OA

{-# LANGUAGE QuantifiedConstraints, DeriveLift #-}
{-# OPTIONS_GHC -Wno-orphans #-}

module Morley.Util.Constrained
  ( Constrained(..)
  , NullConstraint
  , mapConstrained
  , traverseConstrained
  , withConstrained
  , withConstrainedM
  , foldConstrained
  , foldConstrainedM
  ) where

import Data.GADT.Compare (GCompare, GEq, defaultCompare, defaultEq)
import Fmt (Buildable(..))
import Language.Haskell.TH.Syntax (Lift)

-- | Always truthful unary constraint. Can be used to essentially turn
-- 'Constrained' into a somewhat inefficient @Some@.
type NullConstraint :: forall k. k -> Constraint
class NullConstraint any
instance NullConstraint any

type Constrained :: (k -> Constraint) -> (k -> Type) -> Type
data Constrained c f where
  Constrained :: forall c f a. c a => f a -> Constrained c f

-- | Map over argument.
mapConstrained :: (forall t. c t => f t -> g t) -> Constrained c f -> Constrained c g
mapConstrained f = foldConstrained $ Constrained . f

-- | Traverse over argument.
traverseConstrained :: Functor m => (forall a. c a => f a -> m (g a)) -> Constrained c f -> m (Constrained c g)
traverseConstrained f = foldConstrained $ fmap Constrained . f

-- | Apply function to constrained value
withConstrained :: Constrained c f -> (forall t. c t => f t -> r) -> r
withConstrained (Constrained x) f = f x

-- | Monadic 'withConstrained'
withConstrainedM :: Monad m => m (Constrained c f) -> (forall t. f t -> m r) -> m r
withConstrainedM m f = m >>= foldConstrained f

-- | Flipped version of 'withConstrained'
foldConstrained :: (forall t. c t => f t -> r) -> Constrained c f -> r
foldConstrained f (Constrained x) = f x

-- | Flipped version of 'withConstrainedM'
foldConstrainedM :: Monad m => (forall t. c t => f t -> m r) -> m (Constrained c f) -> m r
foldConstrainedM f m = m >>= foldConstrained f

deriving stock instance (forall a. c a => Show (f a)) => Show (Constrained c f)

instance (forall a. c a => NFData (f a)) => NFData (Constrained c f) where
  rnf (Constrained x) = rnf x

deriving stock instance (forall a. c a => Lift (f a)) => Lift (Constrained c f)

instance (forall a. c a => Buildable (f a)) => Buildable (Constrained c f) where
  build (Constrained a) = build a

instance GEq f => Eq (Constrained c f) where
  (Constrained a) == (Constrained b) = defaultEq a b

instance GCompare f => Ord (Constrained c f) where
  compare (Constrained a) (Constrained b) = defaultCompare a b