Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can CString be an instance of IsString, Show?

Tags:

haskell

There's

newCString :: String -> IO CString
peekCString :: CString -> IO String

And you need

fromString :: String -> a
show :: a -> String

My lame attempt

{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE OverlappingInstances #-}

module Main where

import Data.String
import Foreign.C.String
import System.IO.Unsafe

instance IsString CString where
  fromString = unsafePerformIO . newCString

instance {-# OVERLAPS #-} Show CString where
  show = unsafePerformIO . peekCString

mycs :: CString
mycs = "hello CString"

main = putStrLn $ show mycs

It seems to work. But, having never uttered unsafePerformIO before I wonder what kind of disaster I'm setting up here? Did I leak memory? Is there a safe approach to this convenience?

like image 618
Michael Fox Avatar asked Jul 24 '15 21:07

Michael Fox


2 Answers

No. Your show function, for example, will return different results for the same CString value at different times. This is because a CString is really a mutable string under the hood, equivalent to IORef String. Because when an expression is evaluated and how many times an expression is evaluated are not specified by the Haskell language, an expression show cs using your instance will have a non-deterministic value, so most Haskell programmers would say this is not a valid use of unsafePerformIO, and it is (intentionally) not possible to implement Show CString without using unsafePerformIO.

like image 134
Jonathan Cast Avatar answered Nov 16 '22 02:11

Jonathan Cast


This may be technically safe since you never ever change the memory pointed to by the CString value. Hence, the values are indeed immutable, and the effect of unsafePerformIO just mimicks a pure value.

However, the above holds only because you never release memory, and simply leak it every time you allocate a new string with newCString.

As soon as you fix the memory leak using free, then you lose safety: unsafePerformIO will read whatever is found at the old memory address. If you are lucky, this will crash the program. If you are unlucky, this will read garbage data into your strings. Good luck in debugging that.

I would strongly recommend against that approach.

TL;DR:

  1. unsafePerformIO is unsafe, don't use it unless you know what you are doing.
  2. C-like pointers such as CString should be used as little as possible, and only to interface with foreign languages.
like image 45
chi Avatar answered Nov 16 '22 01:11

chi