
Yes, I ask dumb questions on haskell-cafe for a reason ;) As the subject already states, I think that it's worth to be considered to use phantom types and type classes to encode the access mode of an attribute. A first approach is the following: ---- Begin 1 ---- class ReadMode mode class WriteMode mode class Mode a data Readable = Readable data Writeable = Writeable data ReadWrite = ReadWrite instance ReadMode Readable instance WriteMode Writeable instance ReadMode ReadWrite instance WriteMode ReadWrite instance ReadMode a => ReadMode (a,b) instance WriteMode b => WriteMode (a,b) data Attr m w a = Attr (w -> IO a) (w -> a -> IO ()) newAttr :: (w -> IO a) -> (w -> a -> IO ()) -> Attr ReadWrite w a readAttr :: (w -> IO a) -> Attr Readable w a writeAttr :: (w -> a -> IO ()) -> Attr Writeable w a get :: ReadMode m => w -> Attr m w a -> IO a ---- End 1 ---- However this has a problem: the combinatorial explosion of data declarations to represent modes, if one has more than two "switches" (as I will have in the next days ;-) The second one, wich I particularly like, is: ---- Begin 2 ---- class ReadMode mode class WriteMode mode class Mode a data Readable = Readable data Writeable = Writeable instance Mode Readable instance Mode Writeable instance ReadMode Readable instance WriteMode Writeable instance (Mode a,Mode b,ReadMode a) => ReadMode (a,b) instance (Mode a,Mode b,WriteMode b) => WriteMode (a,b) data Attr m w a = Attr (w -> IO a) (w -> a -> IO ()) newAttr :: (w -> IO a) -> (w -> a -> IO ()) -> Attr (Readable,Writeable) w a readAttr :: (w -> IO a) -> Attr Readable w a writeAttr :: (w -> a -> IO ()) -> Attr Writeable w a data Prop ... (=:) :: WriteMode m => Attr m w a -> a -> Prop w get :: ReadMode m => w -> Attr m w a -> IO a set :: w -> [Prop w] -> IO () ---- End 2 ---- Note that this is everything haskell98, and can be used (yes, I still haven't tried to recompile htoolkit, but going to try now) like Attr.hs is used right now. This also can solve the already mentioned problem wich is: "I want to breakdown my types in a class hierarchy, so I want to define thing that, say, have a position. So I want to put the type "Menu" in that class, and also the type "Window". But a window can be repositioned, while a menu can't. So I have to make two classes, if I have separated "ReadOnlyAttr" and "ReadWriteAttr" at the type level." To solve this problem, unfortunately, I need multi-parameter type classes, wich could be considered almost legal, but also functional dependencies if I want to avoid type declarations everywhere (and even in places where they aren't haskell98 anyway): ---- Begin 3 ---- class HasMyAttr w m | w -> m where myAttr :: Attr m w () data TestRW = TestRW data TestR = TestR instance HasMyAttr TestRW (Readable,Writeable) where myAttr = newAttr undefined undefined instance HasMyAttr TestR Readable where myAttr = readAttr undefined t1 = set1 TestRW (myAttr =: ()) t2 = get TestRW myAttr t3 = set1 TestR (myAttr =: ()) -- this row can't be compiled t4 = get TestR myAttr ---- End 3 ---- But note how self documenting this code is: the two instance declarations say exactly that "myAttr" is readable and writeable in type TestRW, and read-only in testR. Besides, note that the mode system is extensible: no one constrains an attribute to have a mode wich is one of the types here mentioned, an user has just to conscientiously put its own modes into the right typeclasses. However, if we wish not to allow extention, we still can in some way. Besides, I think that this mode system could be used even if one does not want h98 extentions: it does not limit in any way (AFAIK) the standard (type-correct) usage wrt the present implementation and adds stronger type-checking. I attach the complete modified Attr.hs for reference and wait hopefully for some comment. Vincenzo