
I found myself writing the code below today, and thought I'd write to see if there's a better way, perhaps one that doesn't use unsafePerformIO. A sample use case for this is a program that reads a FilePath from a Handle, and then operates on that file on disk. GHC uses a special encoding for FilePaths that cleanly roundtrips invalid utf8, but for that to work, the FilePath must be read using that encoding too. Neglecting to do that would result in a program that worked for most filenames, but failed on edge case non-utf8 encoded filenames. Another way to deal with this problem is to first hSetEncoding using GHC.IO.Encoding.getFileSystemEncoding, and then read from the Handle using hGetLine, which applies the encoding to the data it reads. However, in this case I needed to use ByteString to read from the Handle, and so needed a way to convert from a ByteString to a FilePath. (Using RawFilePath would be another approach, but reworking my program to use it isn't practical.) Anyway, it seems there should be a pure way to encode a string with GHC's filesystem encoding. ----- import qualified GHC.Foreign as GHC import qualified GHC.IO.Encoding as Encoding import System.IO.Unsafe import Data.Bits.Utils -- from MissingH {- Converts a [Word8] to a FilePath, encoding using the filesystem encoding. - - w82c produces a String, which may contain Chars that are invalid - unicode. From there, this is really a simple matter of applying the - file system encoding, only complicated by GHC's interface to doing so. -} {-# NOINLINE encodeW8 #-} encodeW8 :: [Word8] -> FilePath encodeW8 w8 = unsafePerformIO $ do enc <- Encoding.getFileSystemEncoding GHC.withCString Encoding.char8 (w82s w8) (GHC.peekCString enc) -- see shy jo