reactive-0.11.5: src/Data/AddBounds.hs
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -Wall #-}
----------------------------------------------------------------------
-- |
-- Module : Data.AddBounds
-- Copyright : (c) Conal Elliott 2008
-- License : GNU AGPLv3 (see COPYING)
--
-- 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)
import Data.AffineSpace
-- 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
-- The definition above is too strict for some uses. Here's a parallel
-- version.
-- Alternatively, make a non-parallel definition here and use 'pmin'
-- instead of 'min' where I want.
-- General recipe for Ord methods: use unamb to try two strategies. The
-- first one, "justB", only examines b. The second one first examines
-- only examines a and then examines both. I take care that the two
-- strategies handle disjoint inputs. I could instead let the second
-- strategy handle the first one redundantly, being careful that they
-- agree.
-- This instance is very like the one Richard Smith (lilac) constructed.
-- It fixes a couple of small bugs and follows a style that helps me see
-- that I'm covering all of the cases with the evaluation order I want.
instance Ord a => Ord (AddBounds a) where
a <= b = justB b `unamb` (a <=* b)
where
justB MaxBound = True
justB _ = undefined
MinBound <=* _ = True
_ <=* MinBound = False
NoBound u <=* NoBound v = u <= v
MaxBound <=* NoBound _ = False
_ <=* MaxBound = undefined
a `min` b = justB b `unamb` (a `min'` b)
where
justB MinBound = MinBound
justB MaxBound = a
justB (NoBound _) = undefined
MinBound `min'` _ = MinBound
MaxBound `min'` v = v
NoBound u `min'` NoBound v = NoBound (u `min` v)
_ `min'` MinBound = undefined
_ `min'` MaxBound = undefined
a `max` b = justB b `unamb` (a `max'` b)
where
justB MaxBound = MaxBound
justB MinBound = a
justB (NoBound _) = undefined
MaxBound `max'` _ = MaxBound
MinBound `max'` v = v
NoBound u `max'` NoBound v = NoBound (u `max` v)
_ `max'` MaxBound = undefined
_ `max'` MinBound = undefined
-- 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 Arbitrary a => Arbitrary (AddBounds a) where
arbitrary = frequency [ (1 ,pure MinBound)
, (10, NoBound <$> arbitrary)
, (1 ,pure MaxBound) ]
instance CoArbitrary a => CoArbitrary (AddBounds a) where
coarbitrary MinBound = variant (0::Int)
coarbitrary (NoBound a) = variant (1::Int) . coarbitrary a
coarbitrary MaxBound = variant (2::Int)
instance (EqProp a, Eq a) => EqProp (AddBounds a) where
NoBound a =-= NoBound b = a =-= b
u =-= v = u `eq` v
-- Hm. I'm dissatisfied with this next instance. I'd like to tweak my
-- type definitions to eliminate these partial definitions.
instance AffineSpace t => AffineSpace (AddBounds t) where
type Diff (AddBounds t) = Diff t
NoBound u .-. NoBound v = u .-. v
-- I don't know what to do here
_ .-. _ = error "(.-.) on AddBounds: only defined on NoBound args"
NoBound u .+^ v = NoBound (u .+^ v)
_ .+^ _ = error "(.+^) on AddBounds: only defined on NoBound args"