Informal discussion about the progression of `base`

Not at all. See the documentation of System.OsPath

Basically, the point of OsString is to just keep whatever OS API (e.g. syscalls) throws back at you without any data transformation. That is what you then want for getArgs.

See the unix package:

getArgs :: IO [PosixString]
getArgs =
  alloca $ \ p_argc ->
  alloca $ \ p_argv -> do
   getProgArgv p_argc p_argv
   p    <- fromIntegral <$> peek p_argc
   argv <- peek p_argv
   peekArray (p - 1) (advancePtr argv 1) >>= mapM (fmap PS . B.packCString)

Compare that with base:

getArgs :: IO [String]
getArgs =
  alloca $ \ p_argc ->
  alloca $ \ p_argv -> do
   getProgArgv p_argc p_argv
   p    <- fromIntegral `liftM` peek p_argc
   argv <- peek p_argv
   enc <- argvEncoding
   peekArray (p - 1) (advancePtr argv 1) >>= mapM (GHC.peekCString enc)

GHC.peekCString here is what causes the disaster, while B.packCString does not to do any decoding.

You might notice that we have PosixString here (oh no, yet another type…). That’s because this allows us to express “posix strings” and “windows strings”, while OsString means “string of the current platform”.

E.g. for tar, we actually need PosixString, even if we’re on windows. All this has panned out nicely so far.


Are they the same as ByteString? No. They’re rather wrappers around ShortByteString (which is unpinned memory).

Again: there are differences, both in properties and implementation, between string types.

3 Likes