(unlines . lines) . (unlines . lines) = (unlines . lines)
>>> 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"]
unescapeArgs "hello\\ \\\"world\\\"\n" == ["hello \"world\""]
splitArgs "--foo=\"C:/Program Files/Bar/" --baz" = ["--foo=C:/Program Files/Bar", "--baz"]
splitArgs "\"-DMSGSTR=\\\"foo bar\\\"\" --baz" = ["-DMSGSTR=\"foo bar\"","--baz"]
>>> 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 recovered by passing the result of lines to the unlines function. Otherwise, unlines appends the missing terminating \n. This makes unlines . lines idempotent:
(unlines . lines) . (unlines . lines) = (unlines . lines)
nice_slice "/" -> [] nice_slice "/foo/bar" -> ["foo", "bar"]
slice_path "/" = ["/"] slice_path "/foo/bar" = ["/foo","bar"] slice_path "..//./" = [".."] slice_path "." = []See unslice_path, realpath, realpath_s.
slice_filename "a.b//./.foo.tar.gz" == ["a.b/.foo","tar","gz"] slice_filename ".x..y." == [".x.", "y."]See unslice_filename, slice_filename'.
>>> lines "" []
>>> lines "\n" [""]
>>> lines "one" ["one"]
>>> lines "one\n" ["one"]
>>> lines "one\n\n" ["one",""]
>>> lines "one\ntwo" ["one","two"]
>>> lines "one\ntwo\n" ["one","two"]Thus lines s contains at least as many elements as newlines in s.
>>> words "Lorem ipsum\ndolor" ["Lorem","ipsum","dolor"]
words "Hello Foundation"