Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Avoid uses of head and tail #25

@RyanGlScott

Description

@RyanGlScott

Compiling glgen with GHC 9.8.1 reveals a number of -Wx-partial warnings caused by uses of the partial head and tail functions:

Details
[1 of 7] Compiling Registry         ( src/Registry.hs, interpreted )

src/Registry.hs:96:3: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘head’
    (imported from Prelude, but defined in Just GHC.List):
    "This is a partial function, it throws an error on empty lists. Use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
   |
96 |   head . filter ((== command) . commandName) $ registryCommands registry
   |   ^^^^

src/Registry.hs:100:3: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘head’
    (imported from Prelude, but defined in Just GHC.List):
    "This is a partial function, it throws an error on empty lists. Use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
    |
100 |   head . filter ((== enum) . enumName) $ registryEnums registry
    |   ^^^^
[2 of 7] Compiling Parser           ( src/Parser.hs, interpreted )

src/Parser.hs:120:27: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘head’
    (imported from Prelude, but defined in Just GHC.List):
    "This is a partial function, it throws an error on empty lists. Use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
    |
120 | parseFile file = return . head =<< runX (readDocument [withRemoveWS yes] file >>> parse)
    |                           ^^^^
[3 of 7] Compiling Utils            ( src/Utils.hs, interpreted )
[4 of 7] Compiling Module           ( src/Module.hs, interpreted )
[5 of 7] Compiling Generator        ( src/Generator.hs, interpreted )

src/Generator.hs:48:47: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘tail’
    (imported from Prelude, but defined in Just GHC.List):
    "This is a partial function, it throws an error on empty lists. Replace it with drop 1, or use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
   |
48 | saneEnum = ("GL_"++) . List.intercalate "_" . tail . splitOn "_"
   |                                               ^^^^

src/Generator.hs:517:46: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘tail’
    (imported from Prelude, but defined in Just GHC.List):
    "This is a partial function, it throws an error on empty lists. Replace it with drop 1, or use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
    |
517 |           [ "gl_" ++ (List.intercalate "_" . tail $ splitOn "_" en)
    |                                              ^^^^

src/Generator.hs:553:26: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘tail’
    (imported from Prelude, but defined in Just GHC.List):
    "This is a partial function, it throws an error on empty lists. Replace it with drop 1, or use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
    |
553 |         | parts@(_:_) <- tail (splitOn "_" en) ->
    |                          ^^^^

src/Generator.hs:576:29: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘head’
    (imported from Prelude, but defined in Just GHC.List):
    "This is a partial function, it throws an error on empty lists. Use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
    |
576 |     . filter (\x -> grp == (head . tail . splitOn "_" $ snd x))
    |                             ^^^^

src/Generator.hs:576:36: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘tail’
    (imported from Prelude, but defined in Just GHC.List):
    "This is a partial function, it throws an error on empty lists. Replace it with drop 1, or use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
    |
576 |     . filter (\x -> grp == (head . tail . splitOn "_" $ snd x))
    |                                    ^^^^

src/Generator.hs:583:12: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘head’
    (imported from Prelude, but defined in Just GHC.List):
    "This is a partial function, it throws an error on empty lists. Use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
    |
583 |     . map (head . tail . splitOn "_" . snd)
    |            ^^^^

src/Generator.hs:583:19: warning: [GHC-63394] [-Wx-partial]
    In the use of ‘tail’
    (imported from Prelude, but defined in Just GHC.List):
    "This is a partial function, it throws an error on empty lists. Replace it with drop 1, or use pattern matching or Data.List.uncons instead. Consider refactoring to use Data.List.NonEmpty."
    |
583 |     . map (head . tail . splitOn "_" . snd)
    |                   ^^^^

Most of this code is probably safe due to the assumption that the inputs are always structured in a certain way. We should document these assumptions and refactor the code to provide more specific error messages if these assumptions are violated.

Once possible thing that we could do to avoid some uses of head and tail is to change the type of Utils.splitOn slightly. Currently, it is:

splitOn :: Eq a => [a] -> [a] -> [[a]]

But I think it could just as well be:

splitOn :: Eq a => [a] -> [a] -> NonEmpty [a] 

If we made this change, then several calls to Prelude.{head,tail} above could be replaced with Data.List.NonEmpty.{head,tail}, which are total. This would require one small change to the behavior of splitOn itself:

-splitOn :: Eq a => [a] -> [a] -> [[a]]
-splitOn _ [] = []
+splitOn :: Eq a => [a] -> [a] -> NonEmpty [a]
+splitOn _ [] = [] :| []

But I think this is worth doing anyway, as the behavior of Utils.splitOn differs from the splitOn functions in the split and text packages when the second argument is empty. Making this change to Utils.splitOn brings it in line with the behavior of the splitOn functions in split and text. (I don't think glgen ever calls splitOn on an empty list, but better safe than sorry.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions