summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Sydney Kerckhove <syd@cs-syd.eu>2021-03-09 09:24:05 +0100
committerHécate Moonlight <kleidukos@crypto-keupone.eu>2021-10-15 06:51:18 +0000
commit7a8171bc9db792b19a2ee4dda9e019de5e24ebe0 (patch)
tree711cd55fa9ca0dc33160b3074dccda9a73dbbbc0
parent481e6b546cdbcb646086cd66f22f588c47e66151 (diff)
downloadhaskell-7a8171bc9db792b19a2ee4dda9e019de5e24ebe0.tar.gz
Insert warnings in the documentation of dangerous functions
-rw-r--r--libraries/base/Data/Foldable.hs11
-rw-r--r--libraries/base/Data/Maybe.hs1
-rw-r--r--libraries/base/GHC/Base.hs3
-rw-r--r--libraries/base/GHC/Conc/Sync.hs5
-rw-r--r--libraries/base/GHC/Exception.hs3
-rw-r--r--libraries/base/GHC/IO/Unsafe.hs4
-rw-r--r--libraries/base/GHC/List.hs16
-rw-r--r--libraries/base/GHC/Real.hs35
8 files changed, 76 insertions, 2 deletions
diff --git a/libraries/base/Data/Foldable.hs b/libraries/base/Data/Foldable.hs
index 21fe7cc72d..24bf13566a 100644
--- a/libraries/base/Data/Foldable.hs
+++ b/libraries/base/Data/Foldable.hs
@@ -375,6 +375,7 @@ class Foldable t where
-- >>> foldl (\a _ -> a) 0 $ repeat 1
-- * Hangs forever *
--
+ -- WARNING: When it comes to lists, you always want to use either 'foldl'' or 'foldr' instead.
foldl :: (b -> a -> b) -> b -> t a -> b
foldl f z t = appEndo (getDual (foldMap (Dual . Endo . flip f) t)) z
-- There's no point mucking around with coercions here,
@@ -614,6 +615,8 @@ class Foldable t where
-- >>> maximum Nothing
-- *** Exception: maximum: empty structure
--
+ -- WARNING: This function is partial for possibly-empty structures like lists.
+ --
-- @since 4.8.0.0
maximum :: forall a . Ord a => t a -> a
maximum = fromMaybe (errorWithoutStackTrace "maximum: empty structure") .
@@ -640,6 +643,8 @@ class Foldable t where
-- >>> minimum Nothing
-- *** Exception: minimum: empty structure
--
+ -- WARNING: This function is partial for possibly-empty structures like lists.
+ --
-- @since 4.8.0.0
minimum :: forall a . Ord a => t a -> a
minimum = fromMaybe (errorWithoutStackTrace "minimum: empty structure") .
@@ -1192,6 +1197,8 @@ msum = asum
-- >>> concat [[1, 2, 3], [4, 5], [6], []]
-- [1,2,3,4,5,6]
--
+-- WARNING: This function takes O(n^2) time in the total number of elements
+-- when the 't' is '[]'.
concat :: Foldable t => t [a] -> [a]
concat xs = build (\c n -> foldr (\x y -> foldr c y x) n xs)
{-# INLINE concat #-}
@@ -1325,6 +1332,8 @@ all p = getAll #. foldMap (All #. p)
--
-- >>> maximumBy (compare `on` length) ["Hello", "World", "!", "Longest", "bar"]
-- "Longest"
+--
+-- WARNING: This function is partial for possibly-empty structures like lists.
-- See Note [maximumBy/minimumBy space usage]
maximumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a
@@ -1347,6 +1356,8 @@ maximumBy cmp = fromMaybe (errorWithoutStackTrace "maximumBy: empty structure")
--
-- >>> minimumBy (compare `on` length) ["Hello", "World", "!", "Longest", "bar"]
-- "!"
+--
+-- WARNING: This function is partial for possibly-empty structures like lists.
-- See Note [maximumBy/minimumBy space usage]
minimumBy :: Foldable t => (a -> a -> Ordering) -> t a -> a
diff --git a/libraries/base/Data/Maybe.hs b/libraries/base/Data/Maybe.hs
index acce0bef52..b2b7b0c06d 100644
--- a/libraries/base/Data/Maybe.hs
+++ b/libraries/base/Data/Maybe.hs
@@ -145,6 +145,7 @@ isNothing _ = False
-- *** Exception: Maybe.fromJust: Nothing
-- ...
--
+-- WARNING: This function is partial. You can use case-matching instead.
fromJust :: HasCallStack => Maybe a -> a
fromJust Nothing = error "Maybe.fromJust: Nothing" -- yuck
fromJust (Just x) = x
diff --git a/libraries/base/GHC/Base.hs b/libraries/base/GHC/Base.hs
index b80fa3d349..e670e42ee8 100644
--- a/libraries/base/GHC/Base.hs
+++ b/libraries/base/GHC/Base.hs
@@ -1379,6 +1379,9 @@ The rules for map work like this.
-- > [x1, ..., xm] ++ [y1, ...] == [x1, ..., xm, y1, ...]
--
-- If the first list is not finite, the result is the first list.
+--
+-- WARNING: This function takes linear time in the number of elements of the
+-- first list.
(++) :: [a] -> [a] -> [a]
{-# NOINLINE [1] (++) #-} -- We want the RULE to fire first.
diff --git a/libraries/base/GHC/Conc/Sync.hs b/libraries/base/GHC/Conc/Sync.hs
index b05fe06f4f..d5fb4868df 100644
--- a/libraries/base/GHC/Conc/Sync.hs
+++ b/libraries/base/GHC/Conc/Sync.hs
@@ -260,6 +260,11 @@ The newly created thread has an exception handler that discards the
exceptions 'BlockedIndefinitelyOnMVar', 'BlockedIndefinitelyOnSTM', and
'ThreadKilled', and passes all other exceptions to the uncaught
exception handler.
+
+WARNING: Exceptions in the new thread will not be rethrown in the thread that
+created it. This means that you might be completely unaware of the problem
+if/when this happens. You may want to use the
+<hackage.haskell.org/package/async async> library instead.
-}
forkIO :: IO () -> IO ThreadId
forkIO action = IO $ \ s ->
diff --git a/libraries/base/GHC/Exception.hs b/libraries/base/GHC/Exception.hs
index c9ba038182..a2a457891a 100644
--- a/libraries/base/GHC/Exception.hs
+++ b/libraries/base/GHC/Exception.hs
@@ -45,6 +45,9 @@ import GHC.Exception.Type
-- | Throw an exception. Exceptions may be thrown from purely
-- functional code, but may only be caught within the 'IO' monad.
+--
+-- WARNING: You may want to use 'throwIO' instead so that your pure code
+-- stays exception-free.
throw :: forall (r :: RuntimeRep). forall (a :: TYPE r). forall e.
Exception e => e -> a
throw e = raise# (toException e)
diff --git a/libraries/base/GHC/IO/Unsafe.hs b/libraries/base/GHC/IO/Unsafe.hs
index 5284dcf887..e6c43e920c 100644
--- a/libraries/base/GHC/IO/Unsafe.hs
+++ b/libraries/base/GHC/IO/Unsafe.hs
@@ -117,6 +117,10 @@ monadic use of references. There is no easy way to make it impossible
once you use 'unsafePerformIO'. Indeed, it is
possible to write @coerce :: a -> b@ with the
help of 'unsafePerformIO'. So be careful!
+
+WARNING: If you're looking for "a way to get a 'String' from an 'IO String'",
+then 'unsafePerformIO' is not the way to go. Learn about do-notation and the
+@<-@ syntax element before you proceed.
-}
unsafePerformIO :: IO a -> a
unsafePerformIO m = unsafeDupablePerformIO (noDuplicate >> m)
diff --git a/libraries/base/GHC/List.hs b/libraries/base/GHC/List.hs
index 658cd2c367..1348d5215f 100644
--- a/libraries/base/GHC/List.hs
+++ b/libraries/base/GHC/List.hs
@@ -64,6 +64,9 @@ infix 4 `elem`, `notElem`
-- 1
-- >>> head []
-- *** Exception: Prelude.head: empty list
+--
+-- WARNING: This function is partial. You can use case-matching, 'uncons' or
+-- 'listToMaybe' instead.
head :: [a] -> a
head (x:_) = x
head [] = badHead
@@ -108,6 +111,9 @@ uncons (x:xs) = Just (x, xs)
-- []
-- >>> tail []
-- *** Exception: Prelude.tail: empty list
+--
+-- WARNING: This function is partial. You can use case-matching or 'uncons'
+-- instead.
tail :: [a] -> [a]
tail (_:xs) = xs
tail [] = errorEmptyList "tail"
@@ -121,6 +127,9 @@ tail [] = errorEmptyList "tail"
-- * Hangs forever *
-- >>> last []
-- *** Exception: Prelude.last: empty list
+--
+-- WARNING: This function is partial. You can use 'reverse' with case-matching,
+-- 'uncons' or 'listToMaybe' instead.
last :: [a] -> a
#if defined(USE_REPORT_PRELUDE)
last [x] = x
@@ -147,6 +156,9 @@ lastError = errorEmptyList "last"
-- []
-- >>> init []
-- *** Exception: Prelude.init: empty list
+--
+-- WARNING: This function is partial. You can use 'reverse' with case-matching
+-- or 'uncons' instead.
init :: [a] -> [a]
#if defined(USE_REPORT_PRELUDE)
init [x] = []
@@ -1296,6 +1308,10 @@ concat = foldr (++) []
-- *** Exception: Prelude.!!: index too large
-- >>> ['a', 'b', 'c'] !! (-1)
-- *** Exception: Prelude.!!: negative index
+--
+-- WARNING: This function is partial. You can use <'atMay'
+-- https://hackage.haskell.org/package/safe-0.3.19/docs/Safe.html#v:atMay>
+-- instead.
(!!) :: [a] -> Int -> a
#if defined(USE_REPORT_PRELUDE)
xs !! n | n < 0 = errorWithoutStackTrace "Prelude.!!: negative index"
diff --git a/libraries/base/GHC/Real.hs b/libraries/base/GHC/Real.hs
index a265150171..e71a91007e 100644
--- a/libraries/base/GHC/Real.hs
+++ b/libraries/base/GHC/Real.hs
@@ -144,20 +144,38 @@ class (Num a, Ord a) => Real a where
-- 'abs'.
class (Real a, Enum a) => Integral a where
-- | integer division truncated toward zero
+ --
+ -- WARNING: This function is partial (because it throws when 0 is passed as
+ -- the divisor) for all the integer types in @base@.
quot :: a -> a -> a
-- | integer remainder, satisfying
--
-- > (x `quot` y)*y + (x `rem` y) == x
+ --
+ -- WARNING: This function is partial (because it throws when 0 is passed as
+ -- the divisor) for all the integer types in @base@.
rem :: a -> a -> a
-- | integer division truncated toward negative infinity
+ --
+ -- WARNING: This function is partial (because it throws when 0 is passed as
+ -- the divisor) for all the integer types in @base@.
div :: a -> a -> a
-- | integer modulus, satisfying
--
-- > (x `div` y)*y + (x `mod` y) == x
+ --
+ -- WARNING: This function is partial (because it throws when 0 is passed as
+ -- the divisor) for all the integer types in @base@.
mod :: a -> a -> a
-- | simultaneous 'quot' and 'rem'
+ --
+ -- WARNING: This function is partial (because it throws when 0 is passed as
+ -- the divisor) for all the integer types in @base@.
quotRem :: a -> a -> (a,a)
-- | simultaneous 'div' and 'mod'
+ --
+ -- WARNING: This function is partial (because it throws when 0 is passed as
+ -- the divisor) for all the integer types in @base@.
divMod :: a -> a -> (a,a)
-- | conversion to 'Integer'
toInteger :: a -> Integer
@@ -575,7 +593,10 @@ instance (Integral a) => Enum (Ratio a) where
-- Coercions
--------------------------------------------------------------
--- | general coercion from integral types
+-- | General coercion from 'Integral' types.
+--
+-- WARNING: This function performs silent truncation if the result type is not
+-- at least as big as the argument's type.
{-# INLINE fromIntegral #-}
-- Inlined to allow built-in rules to match.
-- See Note [Optimising conversions between numeric types]
@@ -583,7 +604,17 @@ instance (Integral a) => Enum (Ratio a) where
fromIntegral :: (Integral a, Num b) => a -> b
fromIntegral = fromInteger . toInteger
--- | general coercion to fractional types
+-- | General coercion to 'Fractional' types.
+--
+-- WARNING: This function goes through the 'Rational' type, which does not have values for 'NaN' for example.
+-- This means it does not round-trip.
+--
+-- For 'Double' it also behaves differently with or without -O0:
+--
+-- > Prelude> realToFrac nan -- With -O0
+-- > -Infinity
+-- > Prelude> realToFrac nan
+-- > NaN
realToFrac :: (Real a, Fractional b) => a -> b
{-# NOINLINE [1] realToFrac #-}
realToFrac = fromRational . toRational