overlapping instances, selecting if type a does not belong to class?

I want to write a class introducing a function which should append a b resulting in a HList containing a and b (order doesn't matter) So I came up with: ============= code =================================================== class (HList c) => HListAppendArbitrary a b c | a b -> c where hAppendArbitrary :: a -> b -> c -- instance HList + HList (1) instance (HList a, HList b, HAppend a b c, HList c) => HListAppendArbitrary a b c where hAppendArbitrary a b = hAppend a b -- overlapping instance HList + value (2) instance (HList a, HList c) => HListAppendArbitrary a b c where hAppendArbitrary a b = HCons b a ============= error ================================================== hps-lib/HPS/Utils.hs|130| 0: || Duplicate instance declarations: || instance [overlap ok] (HList a, HList b, HAppend a b c, HList c) => || HListAppendArbitrary a b c -- Defined at hps-lib/HPS/Utils.hs|130| 0 || instance [overlap ok] (HList a, HList c) => || HListAppendArbitrary a b c -- Defined at hps-lib/HPS/Utils.hs|134| 0 ============= ======================================================= instance (2) should be used if b does not belong to class HList. Of course there is another opportunity by writing (HCons a x) instead of to force the first type beeing a HList... Which is the topic to read from the ghc/ haskell manual ? Marc Weber

Marc Weber wrote:
class (HList c) => HListAppendArbitrary a b c | a b -> c where hAppendArbitrary :: a -> b -> c
-- instance HList + HList (1) instance (HList a, HList b, HAppend a b c, HList c) => HListAppendArbitrary a b c where hAppendArbitrary a b = hAppend a b
-- overlapping instance HList + value (2) instance (HList a, HList c) => HListAppendArbitrary a b c where hAppendArbitrary a b = HCons b a
In Haskell, instances are selected based on the _type_ (rather than class) and only the instance head is considered in the process. The instance constraints are checked only _after_ the instance has been selected, but not during the process. In the above example, if we disregard the instance constraints, the instances become instance ListAppendArbitrary a b c instance ListAppendArbitrary a b c they are indeed the same: hence the typechecker complaint. That said, it is quite possible in Haskell to achieve genuine class-based dispatch, with backtracking if necessary: http://pobox.com/~oleg/ftp/Haskell/poly2.txt However, it seems that your particular problem can be solved with simpler means:
instance (HList a) => HListAppendArbitrary a HNil a where hAppendArbitrary a _ = a instance (HList a, HList b, HList c) => HListAppendArbitrary a (HCons b d) c where hAppendArbitrary a b = hAppend a b -- or use HCons with recursion
-- overlapping instance HList + value (2) instance (HList a, HList c) => HListAppendArbitrary a b c where hAppendArbitrary a b = HCons b a
Besides, the constraint |HList a| is equivalent to |a| being either HNil or HCons x y. Using the structural induction is always preferable. Incidentally, the code in your previous messages contained -fallow-incoherent-instances. I'd like to caution against using this extension. It makes GHC commit to a general instance even if more specific exist (and could be selected if more type information becomes available). Therefore, enabling this extension may lead to surprises. Sometimes this extension is indeed necessary and essential, when we wish to compare type variables for identity. These cases are rare however.

Wow.
That said, it is quite possible in Haskell to achieve genuine class-based dispatch, with backtracking if necessary: http://pobox.com/~oleg/ftp/Haskell/poly2.txt Thanks for digging this up. I'll have to reread it tomorrow. I wasn't able to find the definition of AllOf(But): quote type Nums = Int :*: Integer :*: AllOf Fractionals :*: HNil type Ords = Bool :*: Char :*: AllOf Nums :*: HNil type Eqs = AllOf (TypeCl OpenEqs) :*: AllOfBut Ords Fractionals :*: HNil
instance (HList a) => HListAppendArbitrary a HNil a where hAppendArbitrary a _ = a instance (HList a, HList b, HList c) => HListAppendArbitrary a (HCons b d) c where hAppendArbitrary a b = hAppend a b -- or use HCons with recursion You are right. But it's more complicated in the sense that it needs two instance declarations compared to one (called much boilerplate in the
However, it seems that your particular problem can be solved with simpler means: article) ...
Incidentally, the code in your previous messages contained -fallow-incoherent-instances. I'd like to caution against using this extension. I agree. It was a stupid >>perhaps this will make it compile<< try and error approach.
Thanks oleg! Marc

However, it seems that your particular problem can be solved with simpler means:
instance (HList a) => HListAppendArbitrary a HNil a where hAppendArbitrary a _ = a instance (HList a, HList b, HList c) => HListAppendArbitrary a (HCons b d) c where hAppendArbitrary a b = hAppend a b -- or use HCons with recursion
-- overlapping instance HList + value (2) instance (HList a, HList c) => HListAppendArbitrary a b c where hAppendArbitrary a b = HCons b a
It looks like this, doesn't it ? (Some class isntance declarations are still missing) ============= code =================================================== class (HList c) => HListAppendArbitrary a b c | a b -> c where hAppendArbitrary :: a -> b -> c -- instances HList + HList instance HListAppendArbitrary HNil HNil HNil where -- line 134 hAppendArbitrary _ _ = HNil instance HListAppendArbitrary (HCons a b) HNil (HCons a b) where hAppendArbitrary a _ = a instance HListAppendArbitrary HNil (HCons a b) (HCons a b) where hAppendArbitrary _ a = a instance HListAppendArbitrary (HCons a b) (HCons b c) (HCons a l) where hAppendArbitrary a b = hAppend a b -- instance HList + value instance HListAppendArbitrary HNil b (HCons b HNil) where -- line 143 hAppendArbitrary _ b = HCons b HNil instance HListAppendArbitrary (HCons a b) c d where hAppendArbitrary a b = hAppend a (HCons b HNil) -- instance value + HList instance HListAppendArbitrary b HNil c where hAppendArbitrary b _ = HCons b HNil instance HListAppendArbitrary a (HCons b c) d where hAppendArbitrary a b = hAppend (HCons a HNil) b ============= end code =============================================== But I'm getting the same error twice: hps-lib/HPS/Utils.hs|134| 0: || Functional dependencies conflict between instance declarations: || instance [incoherent] HListAppendArbitrary HNil HNil HNil -- Defined at hps-lib/HPS/Utils.hs|134| 0 || instance [incoherent] HListAppendArbitrary HNil b (HCons b HNil) -- Defined at hps-lib/HPS/Utils.hs|143| 0 If you compare those two lines g instance HListAppendArbitrary HNil HNil HNil where -- line 134 instance HListAppendArbitrary HNil b (HCons b HNil) where -- line 143 I see that HNil HNil -> HNil doesn't fit HNil b (thus maybe HNil) -> HCons b HNil But I don't want to omit the funtcional dependency because the resulting type should be determined by the first two parameters. As I don't know how to solve this right now I'll use HLists anywhere. Marc
participants (2)
-
Marc Weber
-
oleg@pobox.com