{-# LANGUAGE UnicodeSyntax #-} {-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE MultiParamTypeClasses #-} -- | Type-indexed runtime-checked properties. module Data.Checked ( Checked , trustMe , trustThat , trustMap , checked , Property(..) , maybeHolds , check ) where import Data.Typeable (Typeable) import Control.DeepSeq (NFData(..)) -- | Wrapper-evidence for property /p/. newtype Checked p v = Checked v deriving Typeable instance NFData v ⇒ NFData (Checked p v) where rnf (Checked v) = rnf v -- | Use when the property can be deduced without a runtime check. trustMe ∷ v → Checked p v trustMe = Checked {-# INLINE trustMe #-} -- | Use when the property can be deduced without a runtime check. trustThat ∷ p → v → Checked p v trustThat _ = Checked {-# INLINE trustThat #-} -- | Apply a fuction that preserves the property to the checked value. trustMap ∷ (v → v) → Checked p v → Checked p v trustMap f (Checked v) = Checked (f v) {-# INLINE trustMap #-} -- | Unwrap the checked value. checked ∷ Checked p v → v checked (Checked v) = v {-# INLINE checked #-} class Property p v where -- | Test if the property holds for the given value. -- The first argument is supposed to be ignored. holds ∷ p → v → Bool -- | Return 'Just' /v/ if /p/ holds and 'Nothing' overwise. maybeHolds ∷ Property p v ⇒ p → v → Maybe v maybeHolds p v | holds p v = Just v | otherwise = Nothing {-# INLINABLE maybeHolds #-} -- | Wrap the value if the property holds. check ∷ ∀ p v . Property p v ⇒ v → Maybe (Checked p v) check v | holds (undefined ∷ p) v = Just (Checked v) | otherwise = Nothing {-# INLINABLE check #-}