Overlapping families (specificity/degrees of freedom)
Gabor Greif
ggreif at gmail.com
Wed Apr 3 21:01:12 CEST 2013
On 3/14/13, Richard Eisenberg <eir at cis.upenn.edu> wrote:
> 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.
Yep.
>
> 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.
Right. Same for Omega. I have even written an issue for post-facto
type inference years ago (
https://code.google.com/p/omega/issues/detail?id=12 :-)
>
> I hope this helps you understand why your code isn't working.
Sure thing.
What I want is a function that can e.g. create the minimal union of
two Symbol singletons, possibly by consulting decidable (in)equality:
{{{ (sketch)
type family MinUnion (a :: Symbol) (b :: Symbol) :: [Symbol]
type instance where
MinUnion a a = '[a]
MinUnion a b = '[a, b]
minUnion :: DecEq a b -> Sing (a :: Symbol) -> Sing (b :: Symbol) ->
Sing (MinUnion a b)
minUnion (Right Refl) a b -> Cons a Nil
minUnion (Left Refl) a b -> Cons a b
}}}
>
> 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:
No, I did not install singletons yet, too many other things around.
But I have a question below...
>
>> {-# 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
>> |])
You appear to be able to lift `Eq a` up to the type level (and
`member` too) as a type function. How do you compare Symbol? Also,will
this give me an `intersect` function at the value level?
>
> 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?
Let's put this question on hold, I'll definitively come back with an
answer later.
Cheers,
Gabor
>
> 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
>
>
More information about the ghc-devs
mailing list