Overlapping families (specificity/degrees of freedom)

Simon Peyton-Jones simonpj at microsoft.com
Fri Mar 15 09:30:25 CET 2013


Richard (or anyone else)

Now that you have taken the time to type this in, I wonder if some of it might usefully be in
	the user manual
and/or
	the Haskell Wiki user-facing supplementary documentation
?

Seems a shame to waste it!

Simon

|  -----Original Message-----
|  From: ghc-devs-bounces at haskell.org [mailto:ghc-devs-bounces at haskell.org] On
|  Behalf Of Richard Eisenberg
|  Sent: 14 March 2013 17:42
|  To: Gabor Greif
|  Cc: ghc-devs
|  Subject: Re: Overlapping families (specificity/degrees of freedom)
|  
|  Hi Gabor,
|  
|  I can't comment specifically on your code, because I'm afraid I don't understand it
|  all. But, I think I can answer your question:
|  
|  GHC will select a type instance branch to use in a given type family application if
|  and only if the following 2 conditions hold:
|    1. There is a substitution from the variables in the branch statement that makes
|  the left-hand side of the branch coincide with the type family application.
|    2. There exists *no* substitution from the variables in the branch statement
|  ***and the variables in the type family application*** that make the left-hand
|  side of the branch coincide with the (substituted) type family application.
|  
|  Another way to state (2) is that the left-hand side of the branch must not *unify*
|  with the type family application. In your example, this means that GHC must be
|  sure that 'r' and 'one' are distinct types before selecting the fourth branch.
|  
|  Why do this? It's a matter of type safety. Say GHC selected the fourth branch just
|  because the third one doesn't apply at the moment, because the type variables in
|  the application are distinct. The problem is that those variables might later be
|  instantiated at the same value, and then the third branch would have applied. You
|  can convince this sort of inconsistency to produce unsafeCoerce.
|  
|  It gets worse. GHC has no internal notion of inequality, so it can't use previous,
|  failed term-level GADT pattern matches to refine its type assumptions. For
|  example:
|  
|  > data G :: * -> * where
|  >   GInt  :: G Int
|  >   GBool :: G Bool
|  >
|  > type family Foo (a :: *) :: *
|  > type instance where
|  >   Foo Int = Char
|  >   Foo a   = Double
|  >
|  > bar :: G a -> Foo a
|  > bar GInt  = 'x'
|  > bar _     = 3.14
|  
|  The last line will fail to typecheck, because GHC doesn't know that the type
|  variable 'a' can't be Int here, even though it's obvious. The only general way I can
|  see to fix this is to have inequality evidence introduced into GHC, and that's a big
|  deal and I don't know if we have the motivation for such a change yet.
|  
|  I hope this helps you understand why your code isn't working.
|  
|  On the other hand, have you looked at the singletons library I wrote last year? The
|  library takes normal code and, using Template Haskell, "singletonizes" it. For
|  example:
|  
|  > {-# LANGUAGE TemplateHaskell, DataKinds, PolyKinds, TypeFamilies, GADTs,
|  >              UndecidableInstances, FlexibleContexts, RankNTypes #-}
|  >
|  > import Data.Singletons
|  >
|  > $(singletons [d|
|  >   member :: Eq a => a -> [a] -> Bool
|  >   member _ [] = False
|  >   member x (h : t) = x == h || member x t
|  >
|  >   intersect :: Eq a => [a] -> [a] -> [a]
|  >   intersect []      _ = []
|  >   intersect (h : t) b = if member h b then h : (intersect t b) else intersect t b
|  >  |])
|  
|  This works on GHC 7.6.1 and HEAD. Does it do what you want?
|  
|  Richard
|  
|  PS: You said Omega has a different policy about type family reduction. How does
|  that deal with the problem I've discussed here?
|  
|  On Mar 14, 2013, at 12:35 PM, Gabor Greif wrote:
|  
|  > Hi Richard,
|  >
|  > I have a question regarding type families with overlapping syntax.
|  >
|  > In below example the last two lines are overlapped, so the first gets selected
|  > when I present type equality witness to GHC:
|  >
|  >> help :: Sing (ls :: Inventory a) -> Sing (r :: Inventory a) -> Sing (l :: a) ->
|  Sing (InterAppend ls r l)
|  >> help l@(Snoc _ _) (Snoc _ r) one | Just Eq <- isSame r one = Snoc l one    ---
|  OKAY
|  >
|  >> type family InterAppend (l' :: Inventory a) (r' :: Inventory a) (one' :: a) ::
|  Inventory a
|  >> type instance where
|  >>  InterAppend Empty r one = Empty
|  >>  InterAppend (More ls l) Empty one = Empty
|  >>  InterAppend (More ls l) (More rs one) one = More (More ls l) one   --- 4
|  degrees of freedom
|  >>  InterAppend (More ls l) (More rs r) one = More ls l     --- 5 degrees of freedom
|  >
|  > However I cannot manage to trigger the last line when *not* presenting
|  > the witness, e.g. in this case:
|  >
|  >> help l@(Snoc _ _) (Snoc _ _) _ = l    --- DOES NOT WORK
|  >
|  > IIRC, when implementing something like this in Omega, the type
|  > function evaluator considered the number of type variables and tried
|  > to match the more constrained leg (i.e. preferred less degrees of
|  > freedom).
|  >
|  > Can you give me a hint how this is supposed to work in your implementation?
|  >
|  > Must I present a type non-equality witness to trigger the fourth leg
|  > of InterAppend above?
|  >
|  > What I am trying to implement is an intersection algorithm for
|  > singleton-typed list witnesses,
|  > roughly
|  >
|  >> intersect :: SingEqual (t :: a) => Sing (lhs :: '[a]) -> Sing (rhs :: '[a]) ->
|  Sing (Intersect lhs rhs)
|  >
|  > ... but I am not there yet, obviously!
|  >
|  > I'd be grateful for any hint,
|  >
|  > cheers,
|  >
|  >    Gabor
|  
|  
|  _______________________________________________
|  ghc-devs mailing list
|  ghc-devs at haskell.org
|  http://www.haskell.org/mailman/listinfo/ghc-devs



More information about the ghc-devs mailing list