Allowing duplicate instances in GHC 6.4

Hi all, I need to use duplicate instances. I read in the documentation on GHC 6.4, that overlapping class instances checks are lazy instead of gready in 6.4. However, my code still gives duplicate instance errors when compiling in GHC 6.4. Is the duplicate instance check still gready? Is there a way to overwrite that behaviour? Right now, I have two instance of a class Datasource. Datasource allows the user to read (key,value) pairs. class Datasource ds k v where ... Now, I wanted to make a special datasource that combines two datasources, namely data JoinedDS left right = JoinedDS left right instance (Datasource left k v) => Datasource (JoinedDS left right) k v where ... instance (Datasource right k v) => Datasource (JoinedDS left right) k v where ... The idea is that when you combine 2 datasources in one JoinedDS, the user can read both types from the JoinedDS. I do not need to allow to combine 2 different datasources that have the same types of (key,value) pairs, so the duplicate instances will not occur and when they do, this will be by mistake. Hence, the two premisses in the instance declaration will never be fulfilled both at the same time and I do not want a duplicate instance error here. Is there a solution to this problem? Thanks, Robert

Robert van Herk wrote:
Hi all,
I need to use duplicate instances. I read in the documentation on GHC 6.4, that overlapping class instances checks are lazy instead of gready in 6.4. However, my code still gives duplicate instance errors when compiling in GHC 6.4.
Is the duplicate instance check still gready? Is there a way to overwrite that behaviour?
Right now, I have two instance of a class Datasource. Datasource allows the user to read (key,value) pairs.
class Datasource ds k v where ...
Now, I wanted to make a special datasource that combines two datasources, namely
data JoinedDS left right = JoinedDS left right instance (Datasource left k v) => Datasource (JoinedDS left right) k v where ...
instance (Datasource right k v) => Datasource (JoinedDS left right) k v where ...
The idea is that when you combine 2 datasources in one JoinedDS, the user can read both types from the JoinedDS. I do not need to allow to combine 2 different datasources that have the same types of (key,value) pairs, so the duplicate instances will not occur and when they do, this will be by mistake. Hence, the two premisses in the instance declaration will never be fulfilled both at the same time and I do not want a duplicate instance error here.
Is there a solution to this problem?
To resolve overlap the HEAD of the instance must be different... Might I suggest: -- as value depends on source and key, requires functional dependancy class Datasource s k v | s k -> v ... data JoinedDS l r = JoinedDS l r instance (Datasource l k v1,Datasource r k v2) => Datasource (JoinedDS l r) k (v1,v2) ... Now a joined datasource resturns a pair of values instead of a single value. Keean.

Keean Schupke wrote:
Robert van Herk wrote:
Hi all,
I need to use duplicate instances. I read in the documentation on GHC 6.4, that overlapping class instances checks are lazy instead of gready in 6.4. However, my code still gives duplicate instance errors when compiling in GHC 6.4.
Is the duplicate instance check still gready? Is there a way to overwrite that behaviour?
Right now, I have two instance of a class Datasource. Datasource allows the user to read (key,value) pairs.
class Datasource ds k v where ...
Now, I wanted to make a special datasource that combines two datasources, namely
data JoinedDS left right = JoinedDS left right instance (Datasource left k v) => Datasource (JoinedDS left right) k v where ...
instance (Datasource right k v) => Datasource (JoinedDS left right) k v where ...
The idea is that when you combine 2 datasources in one JoinedDS, the user can read both types from the JoinedDS. I do not need to allow to combine 2 different datasources that have the same types of (key,value) pairs, so the duplicate instances will not occur and when they do, this will be by mistake. Hence, the two premisses in the instance declaration will never be fulfilled both at the same time and I do not want a duplicate instance error here.
Is there a solution to this problem?
To resolve overlap the HEAD of the instance must be different... Might I suggest:
-- as value depends on source and key, requires functional dependancy class Datasource s k v | s k -> v ... data JoinedDS l r = JoinedDS l r instance (Datasource l k v1,Datasource r k v2) => Datasource (JoinedDS l r) k (v1,v2) ...
Now a joined datasource resturns a pair of values instead of a single value.
Further to this to get the exact behaviour you want, if a datasource can return the result using a type lifted maybe on a lookup failure then:
class Datasource s k v | s k -> v ... data JoinedDS l r = JoinedDS l r instance (Datasource l k v1, Datasource r k v2, JoinDS v1 v2 v) => Datasource (JoinedDS l r) k v
class Fail data This_should_never_happen
data TNothing = TNothing data TJust a = TJust a
class JoinDS l r t | l r -> t instance JoinDS TNothing TNothing TNothing instance JoinDS TNothing (TJust v) (TJust v) instance JoinDS (TJust u) TNothing (TJust u) instance Fail This_should_never_happen => JoinDS (TJust u) (TJust v) TNothing
Now you datasources just need to return the type "TJust v" on success and TNothing on failure. Keean.

Keean Schupke wrote:
Robert van Herk wrote:
Hi all,
I need to use duplicate instances. I read in the documentation on GHC 6.4, that overlapping class instances checks are lazy instead of gready in 6.4. However, my code still gives duplicate instance errors when compiling in GHC 6.4.
Is the duplicate instance check still gready? Is there a way to overwrite that behaviour?
Right now, I have two instance of a class Datasource. Datasource allows the user to read (key,value) pairs.
class Datasource ds k v where ...
Now, I wanted to make a special datasource that combines two datasources, namely
data JoinedDS left right = JoinedDS left right instance (Datasource left k v) => Datasource (JoinedDS left right) k v where ...
instance (Datasource right k v) => Datasource (JoinedDS left right) k v where ...
The idea is that when you combine 2 datasources in one JoinedDS, the user can read both types from the JoinedDS. I do not need to allow to combine 2 different datasources that have the same types of (key,value) pairs, so the duplicate instances will not occur and when they do, this will be by mistake. Hence, the two premisses in the instance declaration will never be fulfilled both at the same time and I do not want a duplicate instance error here.
Is there a solution to this problem?
To resolve overlap the HEAD of the instance must be different... Might I suggest:
-- as value depends on source and key, requires functional dependancy class Datasource s k v | s k -> v ...
Yes, I already had that, forgot to mention it though...
data JoinedDS l r = JoinedDS l r instance (Datasource l k v1,Datasource r k v2) => Datasource (JoinedDS l r) k (v1,v2) ...
Now a joined datasource resturns a pair of values instead of a single value.
Yes, but this is not what I want. I want to be able to give a key that either the left or the right data source would take, and then return the appropriate value. Thus: if I pass it a key that would normally go into l, I want the value l returns me to be returned, and if I pass it the key that would normally go into r, I want to return the value r returns me. The datasource class has a function dsread :: ds -> k -> (ds, v) -- read may have a side effect Thus I want want to do something like: instance (Datasource l k v) => Datasource (JoinedDS l r) k v where dsread (JoinedDS l r) k = let (l, v) = dsread l k in (JoinedDS l r, v) instance (Datasource r k v) => Datasource (JoinedDS l r) k v where dsread (JoinedDS l r) k = let (r, v) = dsread r k in (JoinedDS l r, v) It would be perfectly okay to me when the compiler would complain if the key and value that go into l and r are the same, but for any useful purpose I can think of (e.g. glueing two database couplings together, since I also made a Datasource instance for database access), this will not happen and the duplicate instances should not really occur, since the context of the instances makes sure only 1 will be possible. However, GHC only looks at the RHS (thus: Datasource (JoinedDS l r) k v) and then decides that both instances are the same. So, my question was: how to overcome this. Thanks, Robert

Just thought I ought to point out that all this is only necessary if the datasources may return different types... If you want them to return the same type you only need: instance (Datasource l k v,Datasource r k v) => Datasource (JoinedDS l r) k v ... As both datasources have the same key and value types, you then choose which 'v' to return at the value level. I am not sure whether you intended Datasources to contain heterogeneous key or value types, and whether the loolup is supposed to be value or type driven. My original answer assumed a single Datasource contains values of different types, selected by the type of the key... Keean. Robert van Herk wrote:
Yes, but this is not what I want. I want to be able to give a key that either the left or the right data source would take, and then return the appropriate value. Thus: if I pass it a key that would normally go into l, I want the value l returns me to be returned, and if I pass it the key that would normally go into r, I want to return the value r returns me.
The datasource class has a function dsread :: ds -> k -> (ds, v) -- read may have a side effect Thus I want want to do something like: instance (Datasource l k v) => Datasource (JoinedDS l r) k v where dsread (JoinedDS l r) k = let (l, v) = dsread l k in (JoinedDS l r, v) instance (Datasource r k v) => Datasource (JoinedDS l r) k v where dsread (JoinedDS l r) k = let (r, v) = dsread r k in (JoinedDS l r, v)
It would be perfectly okay to me when the compiler would complain if the key and value that go into l and r are the same, but for any useful purpose I can think of (e.g. glueing two database couplings together, since I also made a Datasource instance for database access), this will not happen and the duplicate instances should not really occur, since the context of the instances makes sure only 1 will be possible.
However, GHC only looks at the RHS (thus: Datasource (JoinedDS l r) k v) and then decides that both instances are the same.
So, my question was: how to overcome this.
Thanks, Robert

Keean Schupke wrote:
Just thought I ought to point out that all this is only necessary if the datasources may return different types... If you want them to return the same type you only need:
instance (Datasource l k v,Datasource r k v) => Datasource (JoinedDS l r) k v ...
As both datasources have the same key and value types, you then choose which 'v' to return at the value level.
Nono, the datasources I have implemented are a type safe means to extract (key,value) pairs from a data store. The idea is that this way, in a type safe fashion, e.g. database access can be abstract. I use HaskellDB as the database access layer, and then define a datasource instance for any given database, so that the user does not need to think about the details of the actual database access: he can just read and write from the datasource, and the datasource will make sure the actual queries will be executed. My idea now was that if I have 2 databases, and I construct datasources for them, it would be really cool if I was able to unite them, so that the programmer in the end could talk two 1 datasource, that allowed for accessing the 2 databases at one entry point. This was what I was making the JoinedDS for. So, suppose I have 2 datasources for two different databases. One may have keys: data KeysOfDS1 = KDB1_Table1 Int | KDB1_Table2 Int and values data ValuesOfDS1 = VDB1_Table1 (Int,Int,String) | VDB2_Table2 (Int,Int,String) and the other one: data KeysOfDS2 = KDB2_Table1 String | KDB2_Table2 String data ValuesOfDS2 = VDB2_Table1 (String, Float) | VDB2_Table2 (String, Float, Int) Now, these datastructures correspond to the actual tables in the database. My toolset will generate datasources for these types, thus we have instances: instance Datasource Database1 KeysOfDS1 ValuesOfDS1 instance Datasource Database2 KeysOfDS2 ValuesOfDS2 and the cool thing would be, to combine these two datasources at a higher level in my datasources graph, so that I would have 1 datasource that found out by itself which actual datasource to use, thus: x::JoinedDS x = JoinedDS db1 db2 -- where dbx is a datasource Databasex KeysOfDSx ValuesOfDSx Now, I would want the user to be able to read both KeysOfDS1 (which would yield a ValuesOfDS1) as well as KeysOfDS2 (which would yield a ValuesOfDS2) from x. Herefore, I need the instances mentioned before: instance (Datasource l k v) => Datasource (JoinedDS l r) k v where dsread (JoinedDS l r) k = let (l, v) = dsread l k in (JoinedDS l r, v) instance (Datasource r k v) => Datasource (JoinedDS l r) k v where dsread (JoinedDS l r) k = let (r, v) = dsread r k in (JoinedDS l r, v) But this, thus, yields duplicate instance errors, which I don't like :-). Robert P.S. Sorry for any typos, I am enjoying a rather nice bottle of wine :-).
I am not sure whether you intended Datasources to contain heterogeneous key or value types, and whether the loolup is supposed to be value or type driven. My original answer assumed a single Datasource contains values of different types, selected by the type of the key...
Keean.
Robert van Herk wrote:
Yes, but this is not what I want. I want to be able to give a key that either the left or the right data source would take, and then return the appropriate value. Thus: if I pass it a key that would normally go into l, I want the value l returns me to be returned, and if I pass it the key that would normally go into r, I want to return the value r returns me.
The datasource class has a function dsread :: ds -> k -> (ds, v) -- read may have a side effect Thus I want want to do something like: instance (Datasource l k v) => Datasource (JoinedDS l r) k v where dsread (JoinedDS l r) k = let (l, v) = dsread l k in (JoinedDS l r, v) instance (Datasource r k v) => Datasource (JoinedDS l r) k v where dsread (JoinedDS l r) k = let (r, v) = dsread r k in (JoinedDS l r, v)
It would be perfectly okay to me when the compiler would complain if the key and value that go into l and r are the same, but for any useful purpose I can think of (e.g. glueing two database couplings together, since I also made a Datasource instance for database access), this will not happen and the duplicate instances should not really occur, since the context of the instances makes sure only 1 will be possible.
However, GHC only looks at the RHS (thus: Datasource (JoinedDS l r) k v) and then decides that both instances are the same.
So, my question was: how to overcome this.
Thanks, Robert

Robert van Herk wrote:
Keean Schupke wrote:
Just thought I ought to point out that all this is only necessary if the datasources may return different types... If you want them to return the same type you only need:
instance (Datasource l k v,Datasource r k v) => Datasource (JoinedDS l r) k v ...
As both datasources have the same key and value types, you then choose which 'v' to return at the value level.
Nono, the datasources I have implemented are a type safe means to extract (key,value) pairs from a data store. The idea is that this way, in a type safe fashion, e.g. database access can be abstract.
I use HaskellDB as the database access layer, and then define a datasource instance for any given database, so that the user does not need to think about the details of the actual database access: he can just read and write from the datasource, and the datasource will make sure the actual queries will be executed.
My idea now was that if I have 2 databases, and I construct datasources for them, it would be really cool if I was able to unite them, so that the programmer in the end could talk two 1 datasource, that allowed for accessing the 2 databases at one entry point. This was what I was making the JoinedDS for.
So, suppose I have 2 datasources for two different databases. One may have keys: data KeysOfDS1 = KDB1_Table1 Int | KDB1_Table2 Int
and values data ValuesOfDS1 = VDB1_Table1 (Int,Int,String) | VDB2_Table2 (Int,Int,String)
and the other one: data KeysOfDS2 = KDB2_Table1 String | KDB2_Table2 String data ValuesOfDS2 = VDB2_Table1 (String, Float) | VDB2_Table2 (String, Float, Int)
Now, these datastructures correspond to the actual tables in the database. My toolset will generate datasources for these types, thus we have instances:
instance Datasource Database1 KeysOfDS1 ValuesOfDS1 instance Datasource Database2 KeysOfDS2 ValuesOfDS2
and the cool thing would be, to combine these two datasources at a higher level in my datasources graph, so that I would have 1 datasource that found out by itself which actual datasource to use, thus:
x::JoinedDS x = JoinedDS db1 db2 -- where dbx is a datasource Databasex KeysOfDSx ValuesOfDSx
Now, I would want the user to be able to read both KeysOfDS1 (which would yield a ValuesOfDS1) as well as KeysOfDS2 (which would yield a ValuesOfDS2) from x.
Herefore, I need the instances mentioned before: instance (Datasource l k v) => Datasource (JoinedDS l r) k v where dsread (JoinedDS l r) k = let (l, v) = dsread l k in (JoinedDS l r, v) instance (Datasource r k v) => Datasource (JoinedDS l r) k v where dsread (JoinedDS l r) k = let (r, v) = dsread r k in (JoinedDS l r, v)
But this, thus, yields duplicate instance errors, which I don't like :-).
Robert
P.S. Sorry for any typos, I am enjoying a rather nice bottle of wine :-).
Thats because they overlap in 'k'. However you can change the fundep:
class Datasource s k v | s -> k v instance Datasource DB1 K1 V1 instance Datasource DB2 K2 V2 instance (Datasource l k' v',TypeEq k k' z,Datasource' z l r k v) => Datasource (JoinedDS l r) k v where
class Datasource' z l r k v | z l r -> k v instance Datasource l k v => Datasource' TTrue l r k v instance Datasource r k v => Datasource' TFalse l r k v
Here I have used TypeEq from the HList library to determine if the type parameter k is the same type as the k' from datasource l. This lets k determine which instance from the other class gets used. Keean.

Hi Keean, First of all, thank you for your answers. I have tried your solution using TypeEq. instance (Datasource l k' v', TypeEq k k' z, Datasource' z l r k v) => Datasource (JoinedDS l r) k v where _dsread (JoinedDS refl refr) k = do { l <- readIORef refl; r <- readIORef refr; (z,l,r,v) <- _dsread' (l,r) k; writeIORef refl l; writeIORef refr r; return (JoinedDS refl refr, v); } class Datasource' z l r k v | l r k -> v where _dsread' :: (l,r) -> k -> IO (z,l,r,Maybe v) _dswrite' :: (l,r) -> k -> v -> IO (z,l,r) instance Datasource l k v => Datasource' HTrue l r k v where _dsread' (l,r) k = do { (l,v) <- _dsread l k; return (hTrue, l, r, v); } instance Datasource r k v => Datasource' HFalse l r k v where _dsread' (l,r) k = do { (r,v) <- _dsread r k; return (hFalse, l, r, v); } This compiles. I cannot, however, include type z in the fundep of Datasource', since this conflicts with Datasource ds k v | ds k -> v. Furthermore, I do not understand how the key and value types of my right datasource (r k v) is bound to the instance of Datasource (JoinedDS l r) k v, since in the premisse (Datasource l k' v', TypeEq k k' z, Datasource' z l r k v), nothing is said about Datasource r k'' v''. However, I could be wrong in this, since Datasource r k v is in the premisse of instance Datasource r k v => Datsource' HFalse l r k v. However, my problem is, that when I use this code: do {joined <- createJoinedDS' x y; (joined,(v::Maybe Int)) <- _dsread joined (2.0::Float); } {- | Easy constructor -} createJoinedDS :: (IORef left) -> (IORef right) -> JoinedDS left right createJoinedDS left right = JoinedDS left right createJoinedDS' :: left -> right -> IO (JoinedDS left right) createJoinedDS' l r = do { left <- newIORef l; right <- newIORef r; return (createJoinedDS left right); } the compiler will complain: Could not deduce (Datasource' z1 l r k v) from the context (Datasource (JoinedDS l r) k v, Datasource l k' v', TypeEq k k' z, Datasource' z l r k v) arising from use of `_dsread'' It seems to be the case that it cannot decide on the type of z. Would you know how to solve this? Regards, Robert
participants (2)
-
Keean Schupke
-
Robert van Herk