diff options
Diffstat (limited to 'libraries/base/GHC/Event')
-rw-r--r-- | libraries/base/GHC/Event/Clock.hsc | 21 | ||||
-rw-r--r-- | libraries/base/GHC/Event/Control.hs | 6 | ||||
-rw-r--r-- | libraries/base/GHC/Event/EPoll.hsc | 7 | ||||
-rw-r--r-- | libraries/base/GHC/Event/Internal.hs | 36 | ||||
-rw-r--r-- | libraries/base/GHC/Event/KQueue.hsc | 69 | ||||
-rw-r--r-- | libraries/base/GHC/Event/Manager.hs | 8 | ||||
-rw-r--r-- | libraries/base/GHC/Event/PSQ.hs | 21 | ||||
-rw-r--r-- | libraries/base/GHC/Event/Poll.hsc | 10 | ||||
-rw-r--r-- | libraries/base/GHC/Event/Thread.hs | 8 | ||||
-rw-r--r-- | libraries/base/GHC/Event/TimerManager.hs | 34 | ||||
-rw-r--r-- | libraries/base/GHC/Event/Unique.hs | 5 |
11 files changed, 125 insertions, 100 deletions
diff --git a/libraries/base/GHC/Event/Clock.hsc b/libraries/base/GHC/Event/Clock.hsc deleted file mode 100644 index 7f98a03cd2..0000000000 --- a/libraries/base/GHC/Event/Clock.hsc +++ /dev/null @@ -1,21 +0,0 @@ -{-# LANGUAGE Trustworthy #-} -{-# LANGUAGE NoImplicitPrelude #-} - -module GHC.Event.Clock - ( getMonotonicTime - , getMonotonicTimeNSec - ) where - -import GHC.Base -import GHC.Real -import Data.Word - --- | Return monotonic time in seconds, since some unspecified starting point -getMonotonicTime :: IO Double -getMonotonicTime = do w <- getMonotonicTimeNSec - return (fromIntegral w / 1000000000) - --- | Return monotonic time in nanoseconds, since some unspecified starting point -foreign import ccall unsafe "getMonotonicNSec" - getMonotonicTimeNSec :: IO Word64 - diff --git a/libraries/base/GHC/Event/Control.hs b/libraries/base/GHC/Event/Control.hs index 9e3940ad19..779d60d5d7 100644 --- a/libraries/base/GHC/Event/Control.hs +++ b/libraries/base/GHC/Event/Control.hs @@ -57,7 +57,9 @@ data ControlMessage = CMsgWakeup | CMsgDie | CMsgSignal {-# UNPACK #-} !(ForeignPtr Word8) {-# UNPACK #-} !Signal - deriving (Eq, Show) + deriving ( Eq -- ^ @since 4.4.0.0 + , Show -- ^ @since 4.4.0.0 + ) -- | The structure used to tell the IO manager thread what to do. data Control = W { @@ -124,7 +126,7 @@ newControl shouldRegister = allocaArray 2 $ \fds -> do -- file after it has been closed. closeControl :: Control -> IO () closeControl w = do - atomicModifyIORef (controlIsDead w) (\_ -> (True, ())) + _ <- atomicSwapIORef (controlIsDead w) True _ <- c_close . fromIntegral . controlReadFd $ w _ <- c_close . fromIntegral . controlWriteFd $ w when (didRegisterWakeupFd w) $ c_setIOManagerWakeupFd (-1) diff --git a/libraries/base/GHC/Event/EPoll.hsc b/libraries/base/GHC/Event/EPoll.hsc index 32bfc3913b..14324bc43d 100644 --- a/libraries/base/GHC/Event/EPoll.hsc +++ b/libraries/base/GHC/Event/EPoll.hsc @@ -161,7 +161,12 @@ newtype ControlOp = ControlOp CInt newtype EventType = EventType { unEventType :: Word32 - } deriving (Show, Eq, Num, Bits, FiniteBits) + } deriving ( Show -- ^ @since 4.4.0.0 + , Eq -- ^ @since 4.4.0.0 + , Num -- ^ @since 4.4.0.0 + , Bits -- ^ @since 4.4.0.0 + , FiniteBits -- ^ @since 4.7.0.0 + ) #{enum EventType, EventType , epollIn = EPOLLIN diff --git a/libraries/base/GHC/Event/Internal.hs b/libraries/base/GHC/Event/Internal.hs index 9b8230c032..5778c6f3fe 100644 --- a/libraries/base/GHC/Event/Internal.hs +++ b/libraries/base/GHC/Event/Internal.hs @@ -36,10 +36,11 @@ import GHC.Base import GHC.Word (Word64) import GHC.Num (Num(..)) import GHC.Show (Show(..)) +import Data.Semigroup.Internal (stimesMonoid) -- | An I\/O event. newtype Event = Event Int - deriving (Eq) + deriving Eq -- ^ @since 4.4.0.0 evtNothing :: Event evtNothing = Event 0 @@ -63,7 +64,7 @@ evtClose = Event 4 eventIs :: Event -> Event -> Bool eventIs (Event a) (Event b) = a .&. b /= 0 --- | @since 4.3.1.0 +-- | @since 4.4.0.0 instance Show Event where show e = '[' : (intercalate "," . filter (not . null) $ [evtRead `so` "evtRead", @@ -72,10 +73,14 @@ instance Show Event where where ev `so` disp | e `eventIs` ev = disp | otherwise = "" --- | @since 4.3.1.0 +-- | @since 4.10.0.0 +instance Semigroup Event where + (<>) = evtCombine + stimes = stimesMonoid + +-- | @since 4.4.0.0 instance Monoid Event where mempty = evtNothing - mappend = evtCombine mconcat = evtConcat evtCombine :: Event -> Event -> Event @@ -92,7 +97,9 @@ evtConcat = foldl' evtCombine evtNothing data Lifetime = OneShot -- ^ the registration will be active for only one -- event | MultiShot -- ^ the registration will trigger multiple times - deriving (Show, Eq) + deriving ( Show -- ^ @since 4.8.1.0 + , Eq -- ^ @since 4.8.1.0 + ) -- | The longer of two lifetimes. elSupremum :: Lifetime -> Lifetime -> Lifetime @@ -100,24 +107,33 @@ elSupremum OneShot OneShot = OneShot elSupremum _ _ = MultiShot {-# INLINE elSupremum #-} +-- | @since 4.10.0.0 +instance Semigroup Lifetime where + (<>) = elSupremum + stimes = stimesMonoid + -- | @mappend@ takes the longer of two lifetimes. -- -- @since 4.8.0.0 instance Monoid Lifetime where mempty = OneShot - mappend = elSupremum -- | A pair of an event and lifetime -- -- Here we encode the event in the bottom three bits and the lifetime -- in the fourth bit. newtype EventLifetime = EL Int - deriving (Show, Eq) + deriving ( Show -- ^ @since 4.8.0.0 + , Eq -- ^ @since 4.8.0.0 + ) + +-- | @since 4.11.0.0 +instance Semigroup EventLifetime where + EL a <> EL b = EL (a .|. b) -- | @since 4.8.0.0 instance Monoid EventLifetime where mempty = EL 0 - EL a `mappend` EL b = EL (a .|. b) eventLifetime :: Event -> Lifetime -> EventLifetime eventLifetime (Event e) l = EL (e .|. lifetimeBit l) @@ -137,7 +153,7 @@ elEvent (EL x) = Event (x .&. 0x7) -- | A type alias for timeouts, specified in nanoseconds. data Timeout = Timeout {-# UNPACK #-} !Word64 | Forever - deriving (Show) + deriving Show -- ^ @since 4.4.0.0 -- | Event notification backend. data Backend = forall a. Backend { @@ -200,7 +216,7 @@ delete :: Backend -> IO () delete (Backend bState _ _ _ bDelete) = bDelete bState {-# INLINE delete #-} --- | Throw an 'IOError' corresponding to the current value of +-- | Throw an 'Prelude.IOError' corresponding to the current value of -- 'getErrno' if the result value of the 'IO' action is -1 and -- 'getErrno' is not 'eINTR'. If the result value is -1 and -- 'getErrno' returns 'eINTR' 0 is returned. Otherwise the result diff --git a/libraries/base/GHC/Event/KQueue.hsc b/libraries/base/GHC/Event/KQueue.hsc index e9c8419ea7..49cf82db14 100644 --- a/libraries/base/GHC/Event/KQueue.hsc +++ b/libraries/base/GHC/Event/KQueue.hsc @@ -28,11 +28,13 @@ available = False import Data.Bits (Bits(..), FiniteBits(..)) import Data.Int +import Data.Maybe ( catMaybes ) import Data.Word (Word16, Word32) import Foreign.C.Error (throwErrnoIfMinus1, eINTR, eINVAL, eNOTSUP, getErrno, throwErrno) import Foreign.C.Types import Foreign.Marshal.Alloc (alloca) +import Foreign.Marshal.Array (withArrayLen) import Foreign.Ptr (Ptr, nullPtr) import Foreign.Storable (Storable(..)) import GHC.Base @@ -85,23 +87,20 @@ delete kq = do return () modifyFd :: KQueue -> Fd -> E.Event -> E.Event -> IO Bool -modifyFd kq fd oevt nevt - | nevt == mempty = do - let !ev = event fd (toFilter oevt) flagDelete noteEOF - kqueueControl (kqueueFd kq) ev - | otherwise = do - let !ev = event fd (toFilter nevt) flagAdd noteEOF - kqueueControl (kqueueFd kq) ev - -toFilter :: E.Event -> Filter -toFilter evt - | evt `E.eventIs` E.evtRead = filterRead - | otherwise = filterWrite +modifyFd kq fd oevt nevt = kqueueControl (kqueueFd kq) evs + where + evs + | nevt == mempty = toEvents fd (toFilter oevt) flagDelete noteEOF + | otherwise = toEvents fd (toFilter nevt) flagAdd noteEOF + +toFilter :: E.Event -> [Filter] +toFilter e = catMaybes [ check E.evtRead filterRead, check E.evtWrite filterWrite ] + where + check e' f = if e `E.eventIs` e' then Just f else Nothing modifyFdOnce :: KQueue -> Fd -> E.Event -> IO Bool -modifyFdOnce kq fd evt = do - let !ev = event fd (toFilter evt) (flagAdd .|. flagOneshot) noteEOF - kqueueControl (kqueueFd kq) ev +modifyFdOnce kq fd evt = + kqueueControl (kqueueFd kq) (toEvents fd (toFilter evt) (flagAdd .|. flagOneshot) noteEOF) poll :: KQueue -> Maybe Timeout @@ -125,7 +124,9 @@ poll kq mtimeout f = do newtype KQueueFd = KQueueFd { fromKQueueFd :: CInt - } deriving (Eq, Show) + } deriving ( Eq -- ^ @since 4.4.0.0 + , Show -- ^ @since 4.4.0.0 + ) data Event = KEvent { ident :: {-# UNPACK #-} !CUIntPtr @@ -138,10 +139,10 @@ data Event = KEvent { , data_ :: {-# UNPACK #-} !CIntPtr #endif , udata :: {-# UNPACK #-} !(Ptr ()) - } deriving Show + } deriving Show -- ^ @since 4.4.0.0 -event :: Fd -> Filter -> Flag -> FFlag -> Event -event fd filt flag fflag = KEvent (fromIntegral fd) filt flag fflag 0 nullPtr +toEvents :: Fd -> [Filter] -> Flag -> FFlag -> [Event] +toEvents fd flts flag fflag = map (\filt -> KEvent (fromIntegral fd) filt flag fflag 0 nullPtr) flts -- | @since 4.3.1.0 instance Storable Event where @@ -168,7 +169,10 @@ instance Storable Event where #{poke struct kevent, udata} ptr (udata ev) newtype FFlag = FFlag Word32 - deriving (Eq, Show, Storable) + deriving ( Eq -- ^ @since 4.4.0.0 + , Show -- ^ @since 4.4.0.0 + , Storable -- ^ @since 4.4.0.0 + ) #{enum FFlag, FFlag , noteEOF = NOTE_EOF @@ -179,7 +183,13 @@ newtype Flag = Flag Word32 #else newtype Flag = Flag Word16 #endif - deriving (Bits, FiniteBits, Eq, Num, Show, Storable) + deriving ( Bits -- ^ @since 4.7.0.0 + , FiniteBits -- ^ @since 4.7.0.0 + , Eq -- ^ @since 4.4.0.0 + , Num -- ^ @since 4.7.0.0 + , Show -- ^ @since 4.4.0.0 + , Storable -- ^ @since 4.4.0.0 + ) #{enum Flag, Flag , flagAdd = EV_ADD @@ -192,7 +202,11 @@ newtype Filter = Filter Int32 #else newtype Filter = Filter Int16 #endif - deriving (Bits, FiniteBits, Eq, Num, Show, Storable) + deriving ( Eq -- ^ @since 4.4.0.0 + , Num -- ^ @since 4.4.0.0 + , Show -- ^ @since 4.4.0.0 + , Storable -- ^ @since 4.4.0.0 + ) filterRead :: Filter filterRead = Filter (#const EVFILT_READ) @@ -222,11 +236,11 @@ instance Storable TimeSpec where kqueue :: IO KQueueFd kqueue = KQueueFd `fmap` throwErrnoIfMinus1 "kqueue" c_kqueue -kqueueControl :: KQueueFd -> Event -> IO Bool -kqueueControl kfd ev = +kqueueControl :: KQueueFd -> [Event] -> IO Bool +kqueueControl kfd evts = withTimeSpec (TimeSpec 0 0) $ \tp -> - withEvent ev $ \evp -> do - res <- kevent False kfd evp 1 nullPtr 0 tp + withArrayLen evts $ \evlen evp -> do + res <- kevent False kfd evp evlen nullPtr 0 tp if res == -1 then do err <- getErrno @@ -255,9 +269,6 @@ kevent safe k chs chlen evs evlen ts | safe = c_kevent k chs (fromIntegral chlen) evs (fromIntegral evlen) ts | otherwise = c_kevent_unsafe k chs (fromIntegral chlen) evs (fromIntegral evlen) ts -withEvent :: Event -> (Ptr Event -> IO a) -> IO a -withEvent ev f = alloca $ \ptr -> poke ptr ev >> f ptr - withTimeSpec :: TimeSpec -> (Ptr TimeSpec -> IO a) -> IO a withTimeSpec ts f | tv_sec ts < 0 = f nullPtr diff --git a/libraries/base/GHC/Event/Manager.hs b/libraries/base/GHC/Event/Manager.hs index 013850b5d2..3ee9116812 100644 --- a/libraries/base/GHC/Event/Manager.hs +++ b/libraries/base/GHC/Event/Manager.hs @@ -110,7 +110,9 @@ data FdData = FdData { data FdKey = FdKey { keyFd :: {-# UNPACK #-} !Fd , keyUnique :: {-# UNPACK #-} !Unique - } deriving (Eq, Show) + } deriving ( Eq -- ^ @since 4.4.0.0 + , Show -- ^ @since 4.4.0.0 + ) -- | Callback invoked on I/O events. type IOCallback = FdKey -> Event -> IO () @@ -120,7 +122,9 @@ data State = Created | Dying | Releasing | Finished - deriving (Eq, Show) + deriving ( Eq -- ^ @since 4.4.0.0 + , Show -- ^ @since 4.4.0.0 + ) -- | The event manager state. data EventManager = EventManager diff --git a/libraries/base/GHC/Event/PSQ.hs b/libraries/base/GHC/Event/PSQ.hs index 976ffe16b3..6e13839491 100644 --- a/libraries/base/GHC/Event/PSQ.hs +++ b/libraries/base/GHC/Event/PSQ.hs @@ -28,7 +28,7 @@ module GHC.Event.PSQ , singleton -- * Insertion - , insert + , unsafeInsertNew -- * Delete/Update , delete @@ -36,7 +36,6 @@ module GHC.Event.PSQ -- * Conversion , toList - , fromList -- * Min , findMin @@ -58,7 +57,7 @@ import GHC.Types (Int) {- -- Use macros to define strictness of functions. --- STRICT_x_OF_y denotes an y-ary function strict in the x-th parameter. +-- STRICT_x_OF_y denotes a y-ary function strict in the x-th parameter. -- We do not use BangPatterns, because they are not in any standard and we -- want the compilers to be compiled by as many compilers as possible. #define STRICT_1_OF_2(fn) fn arg _ | arg `seq` False = undefined @@ -213,14 +212,7 @@ singleton = Tip -- Insertion ------------------------------------------------------------------------------ --- | /O(min(n,W))/ Insert a new key, priority and value into the queue. If the key --- is already present in the queue, the associated priority and value are --- replaced with the supplied priority and value. -insert :: Key -> Prio -> v -> IntPSQ v -> IntPSQ v -insert k p x t0 = unsafeInsertNew k p x (delete k t0) - --- | Internal function to insert a key that is *not* present in the priority --- queue. +-- | /O(min(n,W))/ Insert a new key that is *not* present in the priority queue. {-# INLINABLE unsafeInsertNew #-} unsafeInsertNew :: Key -> Prio -> v -> IntPSQ v -> IntPSQ v unsafeInsertNew k p x = go @@ -340,13 +332,6 @@ binShrinkR k p x m l r = Bin k p x m l r -- Lists ------------------------------------------------------------------------------ --- | /O(n*min(n,W))/ Build a queue from a list of (key, priority, value) tuples. --- If the list contains more than one priority and value for the same key, the --- last priority and value for the key is retained. -{-# INLINABLE fromList #-} -fromList :: [Elem v] -> IntPSQ v -fromList = foldr (\(E k p x) im -> insert k p x im) empty - -- | /O(n)/ Convert a queue to a list of (key, priority, value) tuples. The -- order of the list is not specified. toList :: IntPSQ v -> [Elem v] diff --git a/libraries/base/GHC/Event/Poll.hsc b/libraries/base/GHC/Event/Poll.hsc index 74525c6b40..1dafd601ec 100644 --- a/libraries/base/GHC/Event/Poll.hsc +++ b/libraries/base/GHC/Event/Poll.hsc @@ -151,10 +151,16 @@ data PollFd = PollFd { pfdFd :: {-# UNPACK #-} !Fd , pfdEvents :: {-# UNPACK #-} !Event , pfdRevents :: {-# UNPACK #-} !Event - } deriving (Show) + } deriving Show -- ^ @since 4.4.0.0 newtype Event = Event CShort - deriving (Eq, Show, Num, Storable, Bits, FiniteBits) + deriving ( Eq -- ^ @since 4.4.0.0 + , Show -- ^ @since 4.4.0.0 + , Num -- ^ @since 4.4.0.0 + , Storable -- ^ @since 4.4.0.0 + , Bits -- ^ @since 4.4.0.0 + , FiniteBits -- ^ @since 4.7.0.0 + ) -- We have to duplicate the whole enum like this in order for the -- hsc2hs cross-compilation mode to work diff --git a/libraries/base/GHC/Event/Thread.hs b/libraries/base/GHC/Event/Thread.hs index d4b679206a..a9d5410d9c 100644 --- a/libraries/base/GHC/Event/Thread.hs +++ b/libraries/base/GHC/Event/Thread.hs @@ -72,7 +72,7 @@ registerDelay usecs = do -- | Block the current thread until data is available to read from the -- given file descriptor. -- --- This will throw an 'IOError' if the file descriptor was closed +-- This will throw an 'Prelude.IOError' if the file descriptor was closed -- while this thread was blocked. To safely close a file descriptor -- that has been used with 'threadWaitRead', use 'closeFdWith'. threadWaitRead :: Fd -> IO () @@ -82,7 +82,7 @@ threadWaitRead = threadWait evtRead -- | Block the current thread until the given file descriptor can -- accept data to write. -- --- This will throw an 'IOError' if the file descriptor was closed +-- This will throw an 'Prelude.IOError' if the file descriptor was closed -- while this thread was blocked. To safely close a file descriptor -- that has been used with 'threadWaitWrite', use 'closeFdWith'. threadWaitWrite :: Fd -> IO () @@ -145,7 +145,7 @@ threadWaitSTM evt fd = mask_ $ do -- The second element of the return value pair is an IO action that can be used -- to deregister interest in the file descriptor. -- --- The STM action will throw an 'IOError' if the file descriptor was closed +-- The STM action will throw an 'Prelude.IOError' if the file descriptor was closed -- while the STM action is being executed. To safely close a file descriptor -- that has been used with 'threadWaitReadSTM', use 'closeFdWith'. threadWaitReadSTM :: Fd -> IO (STM (), IO ()) @@ -157,7 +157,7 @@ threadWaitReadSTM = threadWaitSTM evtRead -- The second element of the return value pair is an IO action that can be used to deregister -- interest in the file descriptor. -- --- The STM action will throw an 'IOError' if the file descriptor was closed +-- The STM action will throw an 'Prelude.IOError' if the file descriptor was closed -- while the STM action is being executed. To safely close a file descriptor -- that has been used with 'threadWaitWriteSTM', use 'closeFdWith'. threadWaitWriteSTM :: Fd -> IO (STM (), IO ()) diff --git a/libraries/base/GHC/Event/TimerManager.hs b/libraries/base/GHC/Event/TimerManager.hs index f3dbb21686..946f2333bf 100644 --- a/libraries/base/GHC/Event/TimerManager.hs +++ b/libraries/base/GHC/Event/TimerManager.hs @@ -43,11 +43,12 @@ import Data.Foldable (sequence_) import Data.IORef (IORef, atomicModifyIORef', mkWeakIORef, newIORef, readIORef, writeIORef) import GHC.Base +import GHC.Clock (getMonotonicTimeNSec) import GHC.Conc.Signal (runHandlers) +import GHC.Enum (maxBound) import GHC.Num (Num(..)) -import GHC.Real (fromIntegral) +import GHC.Real (quot, fromIntegral) import GHC.Show (Show(..)) -import GHC.Event.Clock (getMonotonicTimeNSec) import GHC.Event.Control import GHC.Event.Internal (Backend, Event, evtRead, Timeout(..)) import GHC.Event.Unique (Unique, UniqueSource, newSource, newUnique) @@ -67,7 +68,7 @@ import qualified GHC.Event.Poll as Poll -- | A timeout registration cookie. newtype TimeoutKey = TK Unique - deriving (Eq) + deriving Eq -- ^ @since 4.7.0.0 -- | Callback invoked on timeout events. type TimeoutCallback = IO () @@ -76,7 +77,9 @@ data State = Created | Running | Dying | Finished - deriving (Eq, Show) + deriving ( Eq -- ^ @since 4.7.0.0 + , Show -- ^ @since 4.7.0.0 + ) -- | A priority search queue, with timeouts as priorities. type TimeoutQueue = Q.PSQ TimeoutCallback @@ -206,6 +209,18 @@ wakeManager mgr = sendWakeup (emControl mgr) ------------------------------------------------------------------------ -- Registering interest in timeout events +expirationTime :: Int -> IO Q.Prio +expirationTime us = do + now <- getMonotonicTimeNSec + let expTime + -- Currently we treat overflows by clamping to maxBound. If humanity + -- still exists in 2500 CE we will ned to be a bit more careful here. + -- See #15158. + | (maxBound - now) `quot` 1000 < fromIntegral us = maxBound + | otherwise = now + ns + where ns = 1000 * fromIntegral us + return expTime + -- | Register a timeout in the given number of microseconds. The -- returned 'TimeoutKey' can be used to later unregister or update the -- timeout. The timeout is automatically unregistered after the given @@ -215,10 +230,11 @@ registerTimeout mgr us cb = do !key <- newUnique (emUniqueSource mgr) if us <= 0 then cb else do - now <- getMonotonicTimeNSec - let expTime = fromIntegral us * 1000 + now + expTime <- expirationTime us - editTimeouts mgr (Q.insert key expTime cb) + -- "unsafeInsertNew" is safe - the key must not exist in the PSQ. It + -- doesn't because we just generated it from a unique supply. + editTimeouts mgr (Q.unsafeInsertNew key expTime cb) return $ TK key -- | Unregister an active timeout. @@ -230,9 +246,7 @@ unregisterTimeout mgr (TK key) = do -- microseconds. updateTimeout :: TimerManager -> TimeoutKey -> Int -> IO () updateTimeout mgr (TK key) us = do - now <- getMonotonicTimeNSec - let expTime = fromIntegral us * 1000 + now - + expTime <- expirationTime us editTimeouts mgr (Q.adjust (const expTime) key) editTimeouts :: TimerManager -> TimeoutEdit -> IO () diff --git a/libraries/base/GHC/Event/Unique.hs b/libraries/base/GHC/Event/Unique.hs index 0363af2068..1339bd97e7 100644 --- a/libraries/base/GHC/Event/Unique.hs +++ b/libraries/base/GHC/Event/Unique.hs @@ -19,7 +19,10 @@ import GHC.Show(Show(..)) data UniqueSource = US (MutableByteArray# RealWorld) newtype Unique = Unique { asInt :: Int } - deriving (Eq, Ord, Num) + deriving ( Eq -- ^ @since 4.4.0.0 + , Ord -- ^ @since 4.4.0.0 + , Num -- ^ @since 4.4.0.0 + ) -- | @since 4.3.1.0 instance Show Unique where |