Skip to content

Commit ce44d3b

Browse files
authored
Change semantics of lines and unlines function to match Haskell and other languages (#1585)
* Add trailing newline on non-empty list in unlines There are several reasons to do that: * a line in a text file is something which ends with newline, and the last line is not special * `unlines []` should be different from `unlines [""]` * `unlines (a ++ b) = unlines a ++ unlines b` * Haskell does it * Change lines function behaviour
1 parent ee063a5 commit ce44d3b

File tree

15 files changed

+82
-21
lines changed

15 files changed

+82
-21
lines changed

libs/base/Data/String.idr

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ foldl1 f (x::xs) = foldl f x xs
4141
-- This uses fastConcat internally so it won't compute at compile time.
4242
export
4343
fastUnlines : List String -> String
44-
fastUnlines = fastConcat . intersperse "\n"
44+
fastUnlines = fastConcat . unlines'
45+
where unlines' : List String -> List String
46+
unlines' [] = []
47+
unlines' (x :: xs) = x :: "\n" :: unlines' xs
4548

4649
-- This is a deprecated alias for fastConcat for backwards compatibility
4750
-- (unfortunately, we don't have %deprecated yet).
@@ -97,34 +100,38 @@ unwords = pack . unwords' . map unpack
97100
||| lines' (unpack "\rA BC\nD\r\nE\n")
98101
||| ```
99102
export
100-
lines' : List Char -> List1 (List Char)
101-
lines' [] = singleton []
102-
lines' s = case break isNL s of
103-
(l, s') => l ::: case s' of
104-
[] => []
105-
_ :: s'' => forget $ lines' (assert_smaller s s'')
103+
lines' : List Char -> List (List Char)
104+
lines' s = linesHelp [] s
105+
where linesHelp : List Char -> List Char -> List (List Char)
106+
linesHelp [] [] = []
107+
linesHelp acc [] = [reverse acc]
108+
linesHelp acc ('\n' :: xs) = reverse acc :: linesHelp [] xs
109+
linesHelp acc ('\r' :: '\n' :: xs) = reverse acc :: linesHelp [] xs
110+
linesHelp acc ('\r' :: xs) = reverse acc :: linesHelp [] xs
111+
linesHelp acc (c :: xs) = linesHelp (c :: acc) xs
112+
106113

107114
||| Splits a string into a list of newline separated strings.
108115
|||
109116
||| ```idris example
110117
||| lines "\rA BC\nD\r\nE\n"
111118
||| ```
112119
export
113-
lines : String -> List1 String
120+
lines : String -> List String
114121
lines s = map pack (lines' (unpack s))
115122

116-
||| Joins the character lists by newlines into a single character list.
123+
||| Joins the character lists by a single character list by appending a newline
124+
||| to each line.
117125
|||
118126
||| ```idris example
119127
||| unlines' [['l','i','n','e'], ['l','i','n','e','2'], ['l','n','3'], ['D']]
120128
||| ```
121129
export
122130
unlines' : List (List Char) -> List Char
123131
unlines' [] = []
124-
unlines' [l] = l
125132
unlines' (l::ls) = l ++ '\n' :: unlines' ls
126133

127-
||| Joins the strings by newlines into a single string.
134+
||| Joins the strings into a single string by appending a newline to each string.
128135
|||
129136
||| ```idris example
130137
||| unlines ["line", "line2", "ln3", "D"]

libs/contrib/Data/String/Extra.idr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ index n str with (unpack str)
9292
||| Indent each line of a given string by `n` spaces.
9393
public export
9494
indentLines : (n : Nat) -> String -> String
95-
indentLines n str = unlines $ map (indent n) $ forget $ lines str
95+
indentLines n str = unlines $ map (indent n) $ lines str
9696

9797
||| Return a string of the given character repeated
9898
||| `n` times.

libs/contrib/System/Console/GetOpt.idr

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,12 @@ fmtOpt : OptDescr a -> List (String,String,String)
9797
fmtOpt (MkOpt sos los ad descr) =
9898
let sosFmt = concat $ intersperse ", " (map (fmtShort ad) sos)
9999
losFmt = concat $ intersperse ", " (map (fmtLong ad) los)
100-
(h ::: t) = lines descr in
100+
(h ::: t) = lines1 descr in
101101
(sosFmt,losFmt,h) :: map (\s => ("","",s)) t
102+
where lines1 : String -> List1 String
103+
lines1 s = case lines s of
104+
[] => "" ::: []
105+
(x :: xs) => x ::: xs
102106

103107
||| Return a string describing the usage of a command, derived from
104108
||| the header (first argument) and the options described by the

libs/contrib/Text/PrettyPrint/Prettyprinter/Doc.idr

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ interface Pretty a where
360360
export
361361
Pretty String where
362362
pretty str = let str' = if "\n" `isSuffixOf` str then dropLast 1 str else str in
363-
vsep $ map unsafeTextWithoutNewLines $ forget $ lines str'
363+
vsep $ map unsafeTextWithoutNewLines $ lines str'
364364

365365
public export
366366
FromString (Doc ann) where

libs/test/Test/Golden.idr

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ options args = case args of
178178
| Nothing => pure (Just opts)
179179
Right only <- readFile fp
180180
| Left err => fail (show err)
181-
pure $ Just $ record { onlyNames $= (forget (lines only) ++) } opts
181+
pure $ Just $ record { onlyNames $= ((lines only) ++) } opts
182182

183183
||| Normalise strings between different OS.
184184
|||
@@ -235,7 +235,7 @@ runTest opts testPath = forkIO $ do
235235
(if opts.color then show . colored BrightRed else id) "FAILURE"
236236
if interactive opts
237237
then mayOverwrite (Just exp) out
238-
else putStrLn . unlines $ expVsOut exp out
238+
else putStr . unlines $ expVsOut exp out
239239

240240
pure $ if result then Right testPath else Left testPath
241241

@@ -266,7 +266,7 @@ runTest opts testPath = forkIO $ do
266266
code <- system $ "git diff --no-index --exit-code " ++
267267
(if opts.color then "--word-diff=color " else "") ++
268268
testPath ++ "/expected " ++ testPath ++ "/output"
269-
putStrLn . unlines $
269+
putStr . unlines $
270270
["Golden value differs from actual value."] ++
271271
(if (code < 0) then expVsOut exp out else []) ++
272272
["Accept actual value as new golden value? [y/N]"]
@@ -454,7 +454,7 @@ poolRunner opts pool
454454
banner msgs = fastUnlines
455455
$ [ "", separator, pool.poolName ]
456456
++ msgs
457-
++ [ separator, "" ]
457+
++ [ separator ]
458458

459459
loop : Options -> Summary -> List String -> IO Summary
460460
loop opts acc [] = pure acc
@@ -489,7 +489,7 @@ runner tests
489489
let list = fastUnlines res.failure
490490
when (nfail > 0) $
491491
do putStrLn "Failing tests:"
492-
putStrLn list
492+
putStr list
493493
-- always overwrite the failure file, if it is given
494494
whenJust opts.failureFile $ \ path =>
495495
do Right _ <- writeFile path list

src/TTImp/Elab/Ambiguity.idr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -368,7 +368,7 @@ checkAlternative rig elabinfo nest env fc (UniqueDefault def) alts mexpected
368368
logGlueNF "elab.ambiguous" 5 (fastConcat
369369
[ "Ambiguous elaboration at ", show fc, ":\n"
370370
, unlines (map show alts)
371-
, "\nWith default. Target type "
371+
, "With default. Target type "
372372
]) env exp'
373373
alts' <- pruneByType env !(getNF exp') alts
374374
log "elab.prune" 5 $
@@ -426,7 +426,7 @@ checkAlternative rig elabinfo nest env fc uniq alts mexpected
426426
, " (", if delayed then "" else "not ", "delayed)"
427427
, " at ", show fc, ":\n"
428428
, unlines (map show alts')
429-
, "\nTarget type "
429+
, "Target type "
430430
]) env exp'
431431
let tryall = case uniq of
432432
FirstSuccess => anyOne fc

tests/Main.idr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ baseLibraryTests = MkTestPool "Base library" [Chez, Node] Nothing
296296
, "system_info_os001"
297297
, "system_system"
298298
, "data_bits001"
299+
, "data_string_lines001"
300+
, "data_string_unlines001"
299301
, "system_errno"
300302
, "system_info001"
301303
, "system_signal001", "system_signal002", "system_signal003", "system_signal004"
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Data.String
2+
3+
main : IO ()
4+
main = do printLn (lines "")
5+
printLn (lines "ab")
6+
printLn (lines "ab\n")
7+
printLn (lines "ab\ncd")
8+
printLn (lines "ab\ncd\n")
9+
printLn (lines "a\rb")
10+
printLn (lines "a\r\nb")
11+
printLn (lines "\n\r\n\n")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
1/1: Building Lines (Lines.idr)
2+
Main> []
3+
["ab"]
4+
["ab"]
5+
["ab", "cd"]
6+
["ab", "cd"]
7+
["a", "b"]
8+
["a", "b"]
9+
["", "", ""]
10+
Main> Bye for now!

tests/base/data_string_lines001/input

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:exec main
2+
:q

0 commit comments

Comments
 (0)