"Marcin 'Qrczak' Kowalczyk" <mk167280@zodiac.mimuw.edu.pl> wrote,
On Sat, 6 Jan 2001, Manuel M. T. Chakravarty wrote:
I think that this is a good idea. Especially, because of the encoding business, Strings are special and it is better to make this explicit in the types.
I'm not sure if this is a good idea. I haven't seen convincing arguments yet. Let's keep it simpler.
The opinion trend for MarshalXXX seems to be clear: Keep it simple. So, ok, let's leave it out.
A Haskell `Char' may expand to a sequence of 8bit `char's. That's a problem when the C side only expects a `char' and not a `*char'.
No library will be able to represent Char or wchar_t in a single char.
Sure. C can choose between char and wchar_t, we only have Char in Haskell. That is what is bothering me.
Could you explain the motivation for these? I can see the need for some way to convert Bool<=>CInt (which is defined by ISO C), but can't the others be just fromIntegral?
One reason for extra functions is that we might want to handle overflows differently.
We would need to use a different interface to signal the overflow. Some exception monad (Maybe or IO). These additional functions don't provide it anyway and I will stick with fromIntegral - the most natural Haskell's way of converting integers.
That's not what I meant. When an overflow is encountered, an `error' can be raised. The point is that during "testing" a binding, I want to see where overflows happen and not just get a core dump later on. Anyway, as I wrote earlier, let's drop MarshalConv.
Now, the current routines in MarshalUtils are overloaded via `Storable', which works fine for the primitives (in fact C2HS uses a default method for the primitives). It becomes interesting for more complex types.
When there is more than one way to pass a complex type, I would just use separate functions if a Storable instance does not fit.
I would represent a pointer to a C string as Ptr CChar in Haskell and use different functions for distinguishing what we want to do with it. Passing the string to C functions is not the only thing we might want to do with it - e.g. we could wish to realloc it, or work on [CChar] in Haskell, or convert it to an aray of bytes, or pass a pointer to the middle to a C function. IMHO it's better and less constraining to be explicit, keep the type and make approprate functions as needed. withCString is not that harder than toAddr. There are too many possible operations to express them in one overloaded function on different types.
I see your point, however, personally, I still prefer to have overloading for the common cases (that what we want most of the time when given a particular type) and use explicit functions only for the exceptions. That makes the resulting code more readable in my opinion - as it contains less clutter for the common case. But I guess, this is a matter of personal style.
For example, remember the case where Marcin had to store a nullFunPtr on the C side and explicitly test for it before calling freeHaskellFunPtr. I think, it was Malcolm who argued that Marcin should better use `Maybe (FunPtr a)' on the Haskell side. One reason that Marcin didn't do that might be that he didn't have ToAddr/FromAddr, but only functions overloaded via `Storable'.
ToAddr/FromAddr would not help here, unless they provided some appropriately overloaded variant of peek.
The definitions that I gave in my previous mail *do* help in this case. I use them myself in C2HS.
[...]
This transparently handles the `Maybe' representation for NULL pointers, without cluttering the marshalling code with special code for `Maybe'.
Passing Maybe as a maybe-null pointer is easy already: maybe ($nullPtr) withCString maybeString $ \ptr -> ...
For the other direction a single function could be added: maybeNull :: (Ptr a -> IO b) -> Ptr a -> IO (Maybe b) maybeNull f ptr | ptr == nullPtr = return Nothing | otherwise = liftM Just (f ptr)
Use it as such: do maybeString <- maybeNull peekCString ptr ...
Yes, let's use `maybeNull' and Sven's `maybeNothing'. Cheers, Manuel