[Git][ghc/ghc][master] 2 commits: Extend documentation for Data.List, mostly wrt infinite lists

Marge Bot (@marge-bot) gitlab at gitlab.haskell.org
Tue Oct 11 22:03:18 UTC 2022



Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC


Commits:
da679f2e by Bodigrim at 2022-10-11T18:02:59-04:00
Extend documentation for Data.List, mostly wrt infinite lists

- - - - -
9c099387 by jwaldmann at 2022-10-11T18:02:59-04:00
Expand comment for Data.List.permutations
- - - - -


3 changed files:

- libraries/base/Data/List.hs
- libraries/base/Data/OldList.hs
- libraries/base/GHC/List.hs


Changes:

=====================================
libraries/base/Data/List.hs
=====================================
@@ -229,14 +229,23 @@ import GHC.Base ( Bool(..), Eq((==)), otherwise )
 --
 -- @since 4.8.0.0
 --
--- ==== __Examples__
---
 -- >>> isSubsequenceOf "GHC" "The Glorious Haskell Compiler"
 -- True
 -- >>> isSubsequenceOf ['a','d'..'z'] ['a'..'z']
 -- True
 -- >>> isSubsequenceOf [1..10] [10,9..0]
 -- False
+--
+-- For the result to be 'True', the first list must be finite;
+-- for the result to be 'False', the second list must be finite:
+--
+-- >>> [0,2..10] `isSubsequenceOf` [0..]
+-- True
+-- >>> [0..] `isSubsequenceOf` [0,2..10]
+-- False
+-- >>> [0,2..] `isSubsequenceOf` [0..]
+-- * Hangs forever*
+--
 isSubsequenceOf :: (Eq a) => [a] -> [a] -> Bool
 isSubsequenceOf []    _                    = True
 isSubsequenceOf _     []                   = False


=====================================
libraries/base/Data/OldList.hs
=====================================
@@ -266,6 +266,7 @@ stripPrefix _ _ = Nothing
 -- | The 'elemIndex' function returns the index of the first element
 -- in the given list which is equal (by '==') to the query element,
 -- or 'Nothing' if there is no such element.
+-- For the result to be 'Nothing', the list must be finite.
 --
 -- >>> elemIndex 4 [0..]
 -- Just 4
@@ -283,6 +284,7 @@ elemIndices x xs = findIndices (x==) xs -- arity 2 so that we don't get a PAP; #
 -- | The 'find' function takes a predicate and a list and returns the
 -- first element in the list matching the predicate, or 'Nothing' if
 -- there is no such element.
+-- For the result to be 'Nothing', the list must be finite.
 --
 -- >>> find (> 4) [1..]
 -- Just 5
@@ -295,6 +297,7 @@ find p          = listToMaybe . filter p
 -- | The 'findIndex' function takes a predicate and a list and returns
 -- the index of the first element in the list satisfying the predicate,
 -- or 'Nothing' if there is no such element.
+-- For the result to be 'Nothing', the list must be finite.
 --
 -- >>> findIndex isSpace "Hello World!"
 -- Just 5
@@ -325,23 +328,41 @@ findIndices p ls = build $ \c n ->
 --
 -- >>> "Hello" `isPrefixOf` "Hello World!"
 -- True
---
 -- >>> "Hello" `isPrefixOf` "Wello Horld!"
 -- False
+--
+-- For the result to be 'True', the first list must be finite;
+-- 'False', however, results from any mismatch:
+--
+-- >>> [0..] `isPrefixOf` [1..]
+-- False
+-- >>> [0..] `isPrefixOf` [0..99]
+-- False
+-- >>> [0..99] `isPrefixOf` [0..]
+-- True
+-- >>> [0..] `isPrefixOf` [0..]
+-- * Hangs forever *
+--
 isPrefixOf              :: (Eq a) => [a] -> [a] -> Bool
 isPrefixOf [] _         =  True
 isPrefixOf _  []        =  False
 isPrefixOf (x:xs) (y:ys)=  x == y && isPrefixOf xs ys
 
 -- | The 'isSuffixOf' function takes two lists and returns 'True' iff
--- the first list is a suffix of the second. The second list must be
--- finite.
+-- the first list is a suffix of the second.
 --
 -- >>> "ld!" `isSuffixOf` "Hello World!"
 -- True
---
 -- >>> "World" `isSuffixOf` "Hello World!"
 -- False
+--
+-- The second list must be finite; however the first list may be infinite:
+--
+-- >>> [0..] `isSuffixOf` [0..99]
+-- False
+-- >>> [0..99] `isSuffixOf` [0..]
+-- * Hangs forever *
+--
 isSuffixOf              :: (Eq a) => [a] -> [a] -> Bool
 ns `isSuffixOf` hs      = maybe False id $ do
       delta <- dropLengthMaybe ns hs
@@ -383,9 +404,19 @@ dropLengthMaybe (_:x') (_:y') = dropLengthMaybe x' y'
 --
 -- >>> isInfixOf "Haskell" "I really like Haskell."
 -- True
---
 -- >>> isInfixOf "Ial" "I really like Haskell."
 -- False
+--
+-- For the result to be 'True', the first list must be finite;
+-- for the result to be 'False', the second list must be finite:
+--
+-- >>> [20..50] `isInfixOf` [0..]
+-- True
+-- >>> [0..] `isInfixOf` [20..50]
+-- False
+-- >>> [0..] `isInfixOf` [0..]
+-- * Hangs forever *
+--
 isInfixOf               :: (Eq a) => [a] -> [a] -> Bool
 isInfixOf needle haystack = any (isPrefixOf needle) (tails haystack)
 
@@ -396,6 +427,12 @@ isInfixOf needle haystack = any (isPrefixOf needle) (tails haystack)
 --
 -- >>> nub [1,2,3,4,3,2,1,2,4,3,5]
 -- [1,2,3,4,5]
+--
+-- If the order of outputs does not matter and there exists @instance Ord a@,
+-- it's faster to use
+-- 'map' @Data.List.NonEmpty.@'Data.List.NonEmpty.head' . @Data.List.NonEmpty.@'Data.List.NonEmpty.group' . 'sort',
+-- which takes only \(\mathcal{O}(n \log n)\) time.
+--
 nub                     :: (Eq a) => [a] -> [a]
 nub                     =  nubBy (==)
 
@@ -454,56 +491,103 @@ deleteBy eq x (y:ys)    = if x `eq` y then ys else y : deleteBy eq x ys
 -- | The '\\' function is list difference (non-associative).
 -- In the result of @xs@ '\\' @ys@, the first occurrence of each element of
 -- @ys@ in turn (if any) has been removed from @xs at .  Thus
---
--- > (xs ++ ys) \\ xs == ys.
+-- @(xs ++ ys) \\\\ xs == ys at .
 --
 -- >>> "Hello World!" \\ "ell W"
 -- "Hoorld!"
 --
 -- It is a special case of 'deleteFirstsBy', which allows the programmer
 -- to supply their own equality test.
-
+--
+-- The second list must be finite, but the first may be infinite.
+--
+-- >>> take 5 ([0..] \\ [2..4])
+-- [0,1,5,6,7]
+-- >>> take 5 ([0..] \\ [2..])
+-- * Hangs forever *
+--
 (\\)                    :: (Eq a) => [a] -> [a] -> [a]
 (\\)                    =  foldl (flip delete)
 
 -- | The 'union' function returns the list union of the two lists.
+-- It is a special case of 'unionBy', which allows the programmer to supply
+-- their own equality test.
 -- For example,
 --
 -- >>> "dog" `union` "cow"
 -- "dogcw"
 --
--- Duplicates, and elements of the first list, are removed from the
--- the second list, but if the first list contains duplicates, so will
--- the result.
--- It is a special case of 'unionBy', which allows the programmer to supply
--- their own equality test.
-
+-- If equal elements are present in both lists, an element from the first list
+-- will be used. If the second list contains equal elements, only the first one
+-- will be retained:
+--
+-- >>> import Data.Semigroup
+-- >>> union [Arg () "dog"] [Arg () "cow"]
+-- [Arg () "dog"]
+-- >>> union [] [Arg () "dog", Arg () "cow"]
+-- [Arg () "dog"]
+--
+-- However if the first list contains duplicates, so will
+-- the result:
+--
+-- >>> "coot" `union` "duck"
+-- "cootduk"
+-- >>> "duck" `union` "coot"
+-- "duckot"
+--
+-- 'union' is productive even if both arguments are infinite.
+--
 union                   :: (Eq a) => [a] -> [a] -> [a]
 union                   = unionBy (==)
 
 -- | The 'unionBy' function is the non-overloaded version of 'union'.
+-- Both arguments may be infinite.
+--
 unionBy                 :: (a -> a -> Bool) -> [a] -> [a] -> [a]
 unionBy eq xs ys        =  xs ++ foldl (flip (deleteBy eq)) (nubBy eq ys) xs
 
 -- | The 'intersect' function takes the list intersection of two lists.
+-- It is a special case of 'intersectBy', which allows the programmer to
+-- supply their own equality test.
 -- For example,
 --
 -- >>> [1,2,3,4] `intersect` [2,4,6,8]
 -- [2,4]
 --
--- If the first list contains duplicates, so will the result.
+-- If equal elements are present in both lists, an element from the first list
+-- will be used, and all duplicates from the second list quashed:
 --
--- >>> [1,2,2,3,4] `intersect` [6,4,4,2]
--- [2,2,4]
+-- >>> import Data.Semigroup
+-- >>> intersect [Arg () "dog"] [Arg () "cow", Arg () "cat"]
+-- [Arg () "dog"]
+--
+-- However if the first list contains duplicates, so will the result.
+--
+-- >>> "coot" `intersect` "heron"
+-- "oo"
+-- >>> "heron" `intersect` "coot"
+-- "o"
+--
+-- If the second list is infinite, 'intersect' either hangs
+-- or returns its first argument in full. Otherwise if the first list
+-- is infinite, 'intersect' might be productive:
+--
+-- >>> intersect [100..] [0..]
+-- [100,101,102,103...
+-- >>> intersect [0] [1..]
+-- * Hangs forever *
+-- >>> intersect [1..] [0]
+-- * Hangs forever *
+-- >>> intersect (cycle [1..3]) [2]
+-- [2,2,2,2...
 --
--- It is a special case of 'intersectBy', which allows the programmer to
--- supply their own equality test. If the element is found in both the first
--- and the second list, the element from the first list will be used.
-
 intersect               :: (Eq a) => [a] -> [a] -> [a]
 intersect               =  intersectBy (==)
 
 -- | The 'intersectBy' function is the non-overloaded version of 'intersect'.
+-- It is productive for infinite arguments only if the first one
+-- is a subset of the second.
+--
 intersectBy             :: (a -> a -> Bool) -> [a] -> [a] -> [a]
 intersectBy _  [] _     =  []
 intersectBy _  _  []    =  []
@@ -547,6 +631,12 @@ intercalate xs xss = concat (intersperse xs xss)
 --
 -- >>> transpose [[10,11],[20],[],[30,31,32]]
 -- [[10,20,30],[11,31],[32]]
+--
+-- For this reason the outer list must be finite; otherwise 'transpose' hangs:
+--
+-- >>> transpose (repeat [])
+-- * Hangs forever *
+--
 transpose :: [[a]] -> [[a]]
 transpose [] = []
 transpose ([] : xss) = transpose xss
@@ -681,7 +771,8 @@ insertBy cmp x ys@(y:ys')
      GT -> y : insertBy cmp x ys'
      _  -> x : ys
 
--- | The 'maximumBy' function takes a comparison function and a list
+-- | The 'maximumBy' function is the non-overloaded version of 'maximum',
+-- which takes a comparison function and a list
 -- and returns the greatest element of the list by the comparison function.
 -- The list must be finite and non-empty.
 --
@@ -697,7 +788,8 @@ maximumBy cmp xs        =  foldl1 maxBy xs
                                        GT -> x
                                        _  -> y
 
--- | The 'minimumBy' function takes a comparison function and a list
+-- | The 'minimumBy' function is the non-overloaded version of 'minimum',
+-- which takes a comparison function and a list
 -- and returns the least element of the list by the comparison function.
 -- The list must be finite and non-empty.
 --
@@ -1045,23 +1137,42 @@ unzip7          =  foldr (\(a,b,c,d,e,f,g) ~(as,bs,cs,ds,es,fs,gs) ->
 
 -- | The 'deleteFirstsBy' function takes a predicate and two lists and
 -- returns the first list with the first occurrence of each element of
--- the second list removed.
+-- the second list removed. This is the non-overloaded version of '(\\)'.
+--
+-- The second list must be finite, but the first may be infinite.
+--
 deleteFirstsBy          :: (a -> a -> Bool) -> [a] -> [a] -> [a]
 deleteFirstsBy eq       =  foldl (flip (deleteBy eq))
 
 -- | The 'group' function takes a list and returns a list of lists such
 -- that the concatenation of the result is equal to the argument.  Moreover,
--- each sublist in the result contains only equal elements.  For example,
+-- each sublist in the result is non-empty and all elements are equal
+-- to the first one.  For example,
 --
 -- >>> group "Mississippi"
 -- ["M","i","ss","i","ss","i","pp","i"]
 --
--- It is a special case of 'groupBy', which allows the programmer to supply
+-- 'group' is a special case of 'groupBy', which allows the programmer to supply
 -- their own equality test.
+--
+-- It's often preferable to use @Data.List.NonEmpty.@'Data.List.NonEmpty.group',
+-- which provides type-level guarantees of non-emptiness of inner lists.
+--
 group                   :: Eq a => [a] -> [[a]]
 group                   =  groupBy (==)
 
 -- | The 'groupBy' function is the non-overloaded version of 'group'.
+--
+-- When a supplied relation is not transitive, it is important
+-- to remember that equality is checked against the first element in the group,
+-- not against the nearest neighbour:
+--
+-- >>> groupBy (\a b -> b - a < 5) [0..19]
+-- [[0,1,2,3,4],[5,6,7,8,9],[10,11,12,13,14],[15,16,17,18,19]]
+--
+-- It's often preferable to use @Data.List.NonEmpty.@'Data.List.NonEmpty.groupBy',
+-- which provides type-level guarantees of non-emptiness of inner lists.
+--
 groupBy                 :: (a -> a -> Bool) -> [a] -> [[a]]
 groupBy _  []           =  []
 groupBy eq (x:xs)       =  (x:ys) : groupBy eq zs
@@ -1078,6 +1189,10 @@ groupBy eq (x:xs)       =  (x:ys) : groupBy eq zs
 --
 -- In particular,
 -- @inits _|_ = [] : _|_@
+--
+-- 'inits' is semantically equivalent to @'map' 'reverse' . 'scanl' ('flip' (:)) []@,
+-- but under the hood uses a queue to amortize costs of 'reverse'.
+--
 inits                   :: [a] -> [[a]]
 inits                   = map toListSB . scanl' snocSB emptySB
 {-# NOINLINE inits #-}
@@ -1106,6 +1221,12 @@ tails lst               =  build (\c n ->
 --
 -- >>> subsequences "abc"
 -- ["","a","b","ab","c","ac","bc","abc"]
+--
+-- This function is productive on infinite inputs:
+--
+-- >>> take 8 $ subsequences ['a'..]
+-- ["","a","b","ab","c","ac","bc","abc"]
+--
 subsequences            :: [a] -> [[a]]
 subsequences xs         =  [] : nonEmptySubsequences xs
 
@@ -1124,7 +1245,26 @@ nonEmptySubsequences (x:xs)  =  [x] : foldr f [] (nonEmptySubsequences xs)
 --
 -- >>> permutations "abc"
 -- ["abc","bac","cba","bca","cab","acb"]
+--
+-- The 'permutations' function is maximally lazy:
+-- for each @n@, the value of @'permutations' xs@ starts with those permutations
+-- that permute @'take' n xs@ and keep @'drop' n xs at .
+-- This function is productive on infinite inputs:
+--
+-- >>> take 6 $ map (take 3) $ permutations ['a'..]
+-- ["abc","bac","cba","bca","cab","acb"]
+--
+-- Note that the order of permutations is not lexicographic.
+-- It satisfies the following property:
+--
+-- > map (take n) (take (product [1..n]) (permutations ([1..n] ++ undefined))) == permutations [1..n]
+--
 permutations            :: [a] -> [[a]]
+-- See https://stackoverflow.com/questions/24484348/what-does-this-list-permutations-implementation-in-haskell-exactly-do/24564307#24564307
+-- for the analysis of this rather cryptic implementation.
+-- Related discussions:
+-- * https://mail.haskell.org/pipermail/haskell-cafe/2021-December/134920.html
+-- * https://mail.haskell.org/pipermail/libraries/2007-December/008788.html
 permutations xs0        =  xs0 : perms xs0 []
   where
     perms []     _  = []
@@ -1147,12 +1287,22 @@ permutations xs0        =  xs0 : perms xs0 []
 --
 -- >>> sort [1,6,4,3,2,5]
 -- [1,2,3,4,5,6]
+--
+-- The argument must be finite.
+--
 sort :: (Ord a) => [a] -> [a]
 
 -- | The 'sortBy' function is the non-overloaded version of 'sort'.
+-- The argument must be finite.
 --
 -- >>> sortBy (\(a,_) (b,_) -> compare a b) [(2, "world"), (4, "!"), (1, "Hello")]
 -- [(1,"Hello"),(2,"world"),(4,"!")]
+--
+-- The supplied comparison relation is supposed to be reflexive and antisymmetric,
+-- otherwise, e. g., for @\_ _ -> GT@, the ordered list simply does not exist.
+-- The relation is also expected to be transitive: if it is not then 'sortBy'
+-- might fail to find an ordered permutation, even if it exists.
+--
 sortBy :: (a -> a -> Ordering) -> [a] -> [a]
 
 #if defined(USE_REPORT_PRELUDE)
@@ -1310,10 +1460,10 @@ rqpart cmp x (y:ys) rle rgt r =
 #endif /* USE_REPORT_PRELUDE */
 
 -- | Sort a list by comparing the results of a key function applied to each
--- element.  @sortOn f@ is equivalent to @sortBy (comparing f)@, but has the
+-- element.  @'sortOn' f@ is equivalent to @'sortBy' ('comparing' f)@, but has the
 -- performance advantage of only evaluating @f@ once for each element in the
 -- input list.  This is called the decorate-sort-undecorate paradigm, or
--- Schwartzian transform.
+-- <https://en.wikipedia.org/wiki/Schwartzian_transform Schwartzian transform>.
 --
 -- Elements are arranged from lowest to highest, keeping duplicates in
 -- the order they appeared in the input.
@@ -1321,6 +1471,8 @@ rqpart cmp x (y:ys) rle rgt r =
 -- >>> sortOn fst [(2, "world"), (4, "!"), (1, "Hello")]
 -- [(1,"Hello"),(2,"world"),(4,"!")]
 --
+-- The argument must be finite.
+--
 -- @since 4.8.0.0
 sortOn :: Ord b => (a -> b) -> [a] -> [a]
 sortOn f =
@@ -1397,35 +1549,29 @@ unfoldr f b0 = build (\c n ->
 -- Functions on strings
 
 -- | Splits the argument into a list of /lines/ stripped of their terminating
--- @\n@ characters.  The @\n@ terminator is optional in a final non-empty
+-- @\\n@ characters.  The @\\n@ terminator is optional in a final non-empty
 -- line of the argument string.
 --
 -- For example:
 --
 -- >>> lines ""           -- empty input contains no lines
 -- []
---
 -- >>> lines "\n"         -- single empty line
 -- [""]
---
 -- >>> lines "one"        -- single unterminated line
 -- ["one"]
---
 -- >>> lines "one\n"      -- single non-empty line
 -- ["one"]
---
 -- >>> lines "one\n\n"    -- second line is empty
 -- ["one",""]
---
 -- >>> lines "one\ntwo"   -- second line is unterminated
 -- ["one","two"]
---
 -- >>> lines "one\ntwo\n" -- two non-empty lines
 -- ["one","two"]
 --
--- When the argument string is empty, or ends in a @\n@ character, it can be
+-- When the argument string is empty, or ends in a @\\n@ character, it can be
 -- recovered by passing the result of 'lines' to the 'unlines' function.
--- Otherwise, 'unlines' appends the missing terminating @\n at .  This makes
+-- Otherwise, 'unlines' appends the missing terminating @\\n at .  This makes
 -- @unlines . lines@ /idempotent/:
 --
 -- > (unlines . lines) . (unlines . lines) = (unlines . lines)
@@ -1443,15 +1589,13 @@ lines s                 =  cons (case break (== '\n') s of
   where
     cons ~(h, t)        =  h : t
 
--- | Appends a @\n@ character to each input string, then concatenates the
--- results. Equivalent to @'foldMap' (\s -> s '++' "\n")@.
+-- | Appends a @\\n@ character to each input string, then concatenates the
+-- results. Equivalent to @'foldMap' (\s -> s '++' "\\n")@.
 --
 -- >>> unlines ["Hello", "World", "!"]
 -- "Hello\nWorld\n!\n"
 --
--- = Note
---
--- @'unlines' '.' 'lines' '/=' 'id'@ when the input is not @\n at -terminated:
+-- Note that @'unlines' '.' 'lines' '/=' 'id'@ when the input is not @\\n at -terminated:
 --
 -- >>> unlines . lines $ "foo\nbar"
 -- "foo\nbar\n"
@@ -1466,10 +1610,14 @@ unlines (l:ls) = l ++ '\n' : unlines ls
 #endif
 
 -- | 'words' breaks a string up into a list of words, which were delimited
--- by white space.
+-- by white space (as defined by 'isSpace'). This function trims any white spaces
+-- at the beginning and at the end.
 --
 -- >>> words "Lorem ipsum\ndolor"
 -- ["Lorem","ipsum","dolor"]
+-- >>> words " foo bar "
+-- ["foo","bar"]
+--
 words                   :: String -> [String]
 {-# NOINLINE [1] words #-}
 words s                 =  case dropWhile {-partain:Char.-}isSpace s of
@@ -1491,11 +1639,18 @@ wordsFB c n = go
              s' -> w `c` go s''
                    where (w, s'') = break isSpace s'
 
--- | 'unwords' is an inverse operation to 'words'.
--- It joins words with separating spaces.
+-- | 'unwords' joins words with separating spaces (U+0020 SPACE).
 --
 -- >>> unwords ["Lorem", "ipsum", "dolor"]
 -- "Lorem ipsum dolor"
+--
+-- 'unwords' is neither left nor right inverse of 'words':
+--
+-- >>> words (unwords [" "])
+-- []
+-- >>> unwords (words "foo\nbar")
+-- "foo bar"
+--
 unwords                 :: [String] -> String
 #if defined(USE_REPORT_PRELUDE)
 unwords []              =  ""


=====================================
libraries/base/GHC/List.hs
=====================================
@@ -801,8 +801,8 @@ iterate'FB c f x0 = go x0
 
 -- | 'repeat' @x@ is an infinite list, with @x@ the value of every element.
 --
--- >>> take 20 $ repeat 17
---[17,17,17,17,17,17,17,17,17...
+-- >>> repeat 17
+-- [17,17,17,17,17,17,17,17,17...
 repeat :: a -> [a]
 {-# INLINE [0] repeat #-}
 -- The pragma just gives the rules more chance to fire
@@ -839,9 +839,9 @@ replicate n x           =  take n (repeat x)
 --
 -- >>> cycle []
 -- *** Exception: Prelude.cycle: empty list
--- >>> take 20 $ cycle [42]
+-- >>> cycle [42]
 -- [42,42,42,42,42,42,42,42,42,42...
--- >>> take 20 $ cycle [2, 5, 7]
+-- >>> cycle [2, 5, 7]
 -- [2,5,7,2,5,7,2,5,7,2,5,7...
 cycle                   :: HasCallStack => [a] -> [a]
 cycle []                = errorEmptyList "cycle"
@@ -1284,6 +1284,7 @@ notElem x (y:ys)=  x /= y && notElem x ys
 
 -- | \(\mathcal{O}(n)\). 'lookup' @key assocs@ looks up a key in an association
 -- list.
+-- For the result to be 'Nothing', the list must be finite.
 --
 -- >>> lookup 2 []
 -- Nothing
@@ -1291,6 +1292,7 @@ notElem x (y:ys)=  x /= y && notElem x ys
 -- Nothing
 -- >>> lookup 2 [(1, "first"), (2, "second"), (3, "third")]
 -- Just "second"
+--
 lookup                  :: (Eq a) => a -> [(a,b)] -> Maybe b
 lookup _key []          =  Nothing
 lookup  key ((x,y):xys)
@@ -1349,8 +1351,8 @@ concat = foldr (++) []
 -- >>> ['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>
+-- WARNING: This function is partial. You can use
+-- <https://hackage.haskell.org/package/safe/docs/Safe.html#v:atMay atMay>
 -- instead.
 #if defined(USE_REPORT_PRELUDE)
 (!!)                    :: [a] -> Int -> a



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/dce9f320ce7275fa97f49abef604abbc3b0f9a9c...9c099387ae82b7bf91232b714a3d35f429d6ffe3

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/dce9f320ce7275fa97f49abef604abbc3b0f9a9c...9c099387ae82b7bf91232b714a3d35f429d6ffe3
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20221011/54e51a10/attachment-0001.html>


More information about the ghc-commits mailing list