4

I'm a Haskell and a Stackoverflow noob, and here's my first & probably quite basic Haskell question.

module M where

import Data.HList

data R r a 

r1 = undefined :: R a Int
r2 = undefined :: R a Double

rPair :: R r a -> R r b -> (R r a, R r b)
rPair = (,)

rp = rPair r1 r2

This makes sense, even if r1 & r2 are polymorphic in r rPair aligns their r type in accordance with the type signature. Is there a technical term for this 'alignment'?

class HList l => RList r l
instance RList r HNil
instance RList r l => RList r (HCons (R r a) l)

rCons :: RList r l => R r a -> l -> (HCons (R r a) l)
rCons = hCons

rc = rCons r1 (rCons r2 hNil)

rCons works great if the R's passed are monomorphic in r, constraining the list's r type as desired. but if they are polymorphic in r it does not align them the way rPair does, and gives an error (defining rc above).

No instance for (RList r (HCons (R r1 Double) HNil))

I have a vague intuition as to why this is the case, but my question is in two parts. Could somebody clearly explain the phenomenon? How would I write an rCons such that the following would hold?

r1 = undefined :: R a Int
r2 = undefined :: R a Double

rc :: HCons (R a Int) (HCons (R a Double) HNil)
rc = rCons r1 (rCons r2 hNil)

Thanks, _c

polypus74
  • 429
  • 4
  • 8
  • update: if i change it to 'class HList l => RList r l | l -> r', with UndecidableInstances and FunctionalDependencies it compiles with rc :: HCons (R GHC.Prim.Any Int) (HCons (R GHC.Prim.Any Double) HNil). now just need to figure out what that actually means – polypus74 Mar 05 '11 at 00:54
  • With functional dependencies, "| l -> r" can often be read as "l uniquely determines r". Does this help your intuition? Also what you've called aligns/alignment I'd call "unifies" Haskell's type system is based on "unification" (just like Prolog's evaluation). – stephen tetley Mar 05 '11 at 08:48
  • Just to reassure anyone reading this, this is not a noob Haskell question. I believe the "alignment" is known as "unification", and the types are said to be "unified". But this is based on what the type checker says when it fails to match up the types, so I could be wrong. – Paul Johnson Mar 05 '11 at 14:09

1 Answers1

1

To answer your second question, you can use type equivalence constraint (from TypeFamilies extension) to relax your RList instance definition:

class HList l => RList r l
instance RList r HNil
instance (RList r1 l, r1 ~ r2) => RList r1 (HCons (R r2 a) l)

Now your rc will be inferred to the desired type.

I don't think I can 'clearly explain' the phenomenon though (somebody surely will), but it's obvious that the difference between rPair and rCons is that while the former binds r type of both arguments to the same type variable, the later doesn't: the second argument is just l constrained with that there should be some instance of RList for that l). Since there is no type signature for rc (note that if you provide one your original example typechecks) and r1 and r2 have polymorphic, not equivalent, r's , compiler is trying to find an instance definition for RList r (HCons (R r1 Double) HNil) (r comes from the 1st argument and r1 - from the 2nd) and fails to do so. With type equivalence constraint we define an instance of RList with two distinct r1 and r2 with the only condition that these needs to be equivalent, so it looks like GHC binds them to the same polymorphic type variable when resolving instance of RList for l.

Ed'ka
  • 6,595
  • 29
  • 30