Here's a way to do pretty much what you're after. The idea is to add an
extra parameter to the Ptr type to indicate if it is a const pointer or
> data Ptr const a<br>
To indicate the constness we create a dummy data type which will show when the pointer type is *not* const.<br>
> data NotConst<br>
Now we can give more refined types to peek and poke like so:<br>
> peek :: Ptr const a -> IO a<br>
> poke :: Ptr NotConst a -> a -> IO ()<br>
With this setup peek will work with both kinds of pointers without any
casting. Constness will also be inferred. If a function is polymorphic
in a const argument that means that it doesn't change to pointer. The
use of higher rank polymorphism can be used to enforce that a pointer
This way of doing it is perhaps not the most beautiful. It would be a
little nicer if we had data kinds as Omega has. And its a shame that we
need to go outside Haskell98 if we want to enforce constness, since we
need to use higher rank polymorphism. Nevertheless this solution
adresses most of issues that you considered.<br>
/Josef<br><br><div><span class="gmail_quote">On 11/2/05, <b class="gmail_sendername">David Roundy</b> <<a href="mailto:firstname.lastname@example.org">email@example.com</a>> wrote:</span><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Hello all,<br><br>I was thinking this morning as I lay on the floor (where I sleep) about<br>static typechecking, and how much more wonderful Haskell is than any other<br>language, when it occurred to me that when working with pointers, Haskell
<br>actually has *less* static typechecking than C or C++. It was a very<br>disturbing thought, so much so that I was almost compelled to arise early<br>to place this question before this learned audience.<br><br>Why is it that in C++ I can write
<br><br>void strcpy(char *dest, const char *src);<br><br>but in Haskell I must import this function as<br><br>> foreign import ccall unsafe "static string.h strcpy"<br>> strcpy :: Ptr CChar -> Ptr CChar -> IO ()
<br><br>and lose that wonderful information that the function doesn't modify the<br>contents of its second argument?<br><br>One could pretty easily create a ConstPtr type which one could peek into,<br>but not poke to, but then you'd have to explicitely convert a Ptr into a
<br>ConstPtr when passing it as an argument. That feels a bit silly.<br><br>One could get around this by introducing a class to get around this<br><br>> class ReadablePtr p where<br>> peek :: p a -> IO a<br>> peekOff ...
<br><br>and then make both Ptr and ConstPtr instances of this class, but this still<br>seems like a very hackish solution.<br><br>Moreover, I'd like to be able to have const objects quite apart from Ptrs,<br>such as a const Handle, which I can read from, but cannot write to, or a
<br>const IORef--and we wouldn't want to leave out const ForeignPtrs. Of<br>course, even reading affects a Handle's internal state, so one would need<br>to be explicit about what "const" indicates. But it seems to me that in
<br>the IO world there are a whole slew of "things that refer to other things"<br>which could all be grouped together.<br><br>And a "const" attribute ought to be derived, so that if I create a data<br>
type<br><br>> data FooPtr = FooPtr String (Ptr Foo)<br><br>one should ideally be able to automatically understand that a const FooPtr<br>holds a const (Ptr Foo).<br><br>One could go further, at least when dealing with Ptrs, and create a way of
<br>handling "restricted" pointers--which we could interpret as a const pointer<br>to an object that cannot be changed by anyone else either. One could<br>safely create restricted pointers with a function of the type
<br><br>> mallocRestrictedPtr :: (Ptr a -> IO ()) -> RestrictedPtr a<br><br>which would allow one to ensure at the typechecking level that<br>RestrictedPtrs point to memory that cannot be modified. There's still some
<br>unstafety involved, in that you could read out of bounds, but you would<br>know that apart from that possibility the contents of a RestrictedPtr truly<br>will never change.<br><br>So my question is, how would one implement such an "annotation" extension?
<br>I'd like to be able to pass a (Ptr a) as a (Const (Ptr a)) without an<br>explicit typecast, since the Const really isn't changing the type of the<br>pointer, it's just marking it as one that can't be modified. A function
<br>that accepts a (Const (Ptr a)) should also accept a (Restricted (Ptr<br>a))--but Restricted pointers are really just pudding, as they only differ<br>from Const pointers in what optimizations are allowed. On the other hand,
<br>it's not just compiler optimizations that they would allow, but also<br>user-code optimizations, which could be much more useful. They also have<br>the advantage of making certain unsafe functions safe.<br><br>The hard part seems to be the lack of a conversion. One could quite easily
<br>implement a<br><br>> data Const a = Const a -- this constructor is *not exported*<br>> toConst :: x -> Const x<br>> unsafeAccessConst :: Const x -> x<br><br>> peek :: Const (Ptr a) -> IO a<br>> peekOff ...
<br><br>etc, and everything would work fine, except that you'd always need to<br>explicitely convert from Ptr to Const Ptr. Perhaps one could make Const be<br>a class as well as a data type:<br><br>> class (Const a) x where
<br>> toConst :: x -> Const a<br>> instance (Const x) x where<br>> toConst = Const<br>> instance (Const x) (Const x) where<br>> toConst = id<br><br>and then one could write code like<br><br>> peek :: Const (cp a) => cp a -> IO a
<br><br>which would move the typecasting burden out of the calling function and<br>into the function that accepts a const argument. Perhaps this would be<br>sufficient, as many data types have only a limited number of "primitive
<br>const" functions, and all the other "const" functions wouldn't actually<br>need to call toConst.<br><br>What this doesn't allow is deriving of constness, so that a Const<br>ForeignPtr would automatically hold a Const Ptr.
<br><br>This whole class+data type scheme seems like it might be useful, but is<br>pretty ugly. Is there a better way this could be done?<br><br>Might one be able to extend the language so that one could add "attribute"
<br>such as Const to data type without changing the the type itself (which<br>would be analogous to what one does in C/C++)?<br>--<br>David Roundy<br><a href="http://www.darcs.net">http://www.darcs.net</a><br>_______________________________________________
<br>Haskell mailing list<br><a href="mailto:Haskell@haskell.org">Haskell@haskell.org</a><br><a href="http://www.haskell.org/mailman/listinfo/haskell">http://www.haskell.org/mailman/listinfo/haskell</a><br></blockquote></div>