reactive-0.9.10: src/Data/AddBounds.hs
{-# OPTIONS_GHC -Wall #-}
----------------------------------------------------------------------
-- |
-- Module : Data.AddBounds
-- Copyright : (c) Conal Elliott 2008
-- License : BSD3
--
-- Maintainer : conal@conal.net
-- Stability : experimental
--
-- Add bounds to an ordered type
----------------------------------------------------------------------
module Data.AddBounds (AddBounds(..)) where
import Control.Applicative (pure,(<$>))
-- import Data.Unamb (unamb)
-- Testing
import Test.QuickCheck
import Test.QuickCheck.Checkers
-- | Wrap a type into one having new least and greatest elements,
-- preserving the existing ordering.
data AddBounds a = MinBound | NoBound a | MaxBound
deriving (Eq {-, Ord-}, Read, Show)
instance Bounded (AddBounds a) where
minBound = MinBound
maxBound = MaxBound
-- Normally, I'd derive 'Ord' as well, but there's a sticky point. The
-- derived instance uses the default definition of 'min', which is uses
-- '(<=)' and thus cannot exploit any partial information. So, define our
-- own 'min' in terms of 'min' on @a@.
-- Examples:
-- (NoBound undefined) `min` (NoBound undefined) can return (NoBound _|_)
-- using this definition, but will not produce any output using the
-- default min.
--
-- (NoBound a) `min` (NoBound b) can return partial information from
-- a `min` b while the default implementation cannot.
instance Ord a => Ord (AddBounds a) where
MinBound <= _ = True
NoBound _ <= MinBound = False
NoBound a <= NoBound b = a <= b
NoBound _ <= MaxBound = True
MaxBound <= MaxBound = True
MaxBound <= _ = False -- given previous
MinBound `min` _ = MinBound
_ `min` MinBound = MinBound
NoBound a `min` NoBound b = NoBound (a `min` b)
u `min` MaxBound = u
MaxBound `min` v = v
MinBound `max` v = v
u `max` MinBound = u
NoBound a `max` NoBound b = NoBound (a `max` b)
_ `max` MaxBound = MaxBound
MaxBound `max` _ = MaxBound
-- Richard Smith (lilac) contributed this code for lazier methods.
-- MaxBound `max` undefined can return full information while the default
-- implementation cannot. And likewise undefined `max` MaxBound.
-- instance Ord a => Ord (AddBounds a) where
-- a <= b = c1 a b `unamb` c2 a b
-- where c1 MinBound _ = True
-- c1 _ MinBound = False
-- c1 (NoBound a') (NoBound b') = a' < b'
-- c1 MaxBound (NoBound _) = False
-- c1 _ _ = undefined
-- c2 _ MaxBound = True
-- c2 _ _ = undefined
-- a `min` b = c1 a b `unamb` c2 a b
-- where c1 MinBound _ = MinBound
-- c1 (NoBound a') (NoBound b') = NoBound $ a' `max` b'
-- c1 (NoBound _ ) MaxBound = a
-- c1 MaxBound (NoBound _ ) = b
-- c1 MaxBound MaxBound = MaxBound
-- c1 _ _ = undefined
-- c2 _ MinBound = MinBound
-- c2 _ _ = undefined
-- a `max` b = c1 a b `unamb` c2 a b
-- where c1 MaxBound _ = MaxBound
-- c1 (NoBound a') (NoBound b') = NoBound $ a' `max` b'
-- c1 (NoBound _ ) MinBound = a
-- c1 MinBound (NoBound _ ) = b
-- c1 MinBound MinBound = MinBound
-- c1 _ _ = undefined
-- c2 _ MaxBound = MaxBound
-- c2 _ _ = undefined
-- This second instance has a strange delays in a reactive-fieldtrip
-- program. My mouse click isn't responded to until I move the mouse.
instance Arbitrary a => Arbitrary (AddBounds a) where
arbitrary = frequency [ (1 ,pure MinBound)
, (10, NoBound <$> arbitrary)
, (1 ,pure MaxBound) ]
coarbitrary MinBound = variant 0
coarbitrary (NoBound a) = variant 1 . coarbitrary a
coarbitrary MaxBound = variant 2
instance (EqProp a, Eq a) => EqProp (AddBounds a) where
NoBound a =-= NoBound b = a =-= b
u =-= v = u `eq` v