Consider the following, where I'm trying to say "a
is a pair":
type family F t a :: Constraint
type instance F Int a = (a ~ (a1, a2))
This doesn't work because both a1
and a2
are not in scope, but is there any way to express this?
Of course in a function signature I can write constraints like (a ~ (a1, a2))
even when a1
and a2
are not mentioned elsewhere, but I need to put this in an instance function signature, which is of course determined by the class. And a
in this case is not a parameter to the instance itself, just the instance function, much like Functor
only has f
as a class parameter, not a
and b
, so I can't add extra constraints to the instance clause.
Yes! You can do this!
First, a couple of useful type families:
type family Fst a where
Fst (x,y) = x
type family Snd a where
Snd (x,y) = y
Now the one you're after:
type IsPair a = a ~ (Fst a, Snd a)
Here's a test:
type family Con a where
Con (f x y) = f
test :: IsPair a => proxy a -> Con a :~: (,)
test _ = Refl
And an even simpler one that turns out to test a stronger property:
test1 :: IsPair a => a -> Fst a
test1 = fst
And just to make sure it's satisfied when it should be:
data Dict c where
Dict :: c => Dict c
test2 :: Dict (IsPair (a, b))
test2 = Dict
You can, of course, use this to define your F
:
type family F t a
type instance F Int a = IsPair a
type family Con a where
Con (f x y) = f
type IsPair a = Con a ~ (,)
The trouble with this one, compared to the first approach, is that it doesn't actually win you the glorious knowledge that a ~ (Fst a, Snd a)
. So it makes a statement, but you can't do a whole heck of a lot with it.
Why just pairs? If you toss PolyKinds
into the mix, you can get very general:
type family Arg1 a where Arg1 (f x) = x
type family Arg2 a where Arg2 (f y x) = y
type family Arg3 a where Arg3 (f z y x) = z
type family Arg4 a where Arg4 (f w z y x) = w
type family IsF (f :: k) (a :: *) :: Constraint
type instance IsF f a = a ~ f (Arg1 a)
type instance IsF f a = a ~ f (Arg2 a) (Arg1 a)
type instance IsF f a = a ~ f (Arg3 a) (Arg2 a) (Arg1 a)
type instance IsF f a = a ~ f (Arg4 a) (Arg3 a) (Arg2 a) (Arg1 a)
You can do this without PolyKinds
too, but then you need IsF1
, IsF2
, etc.
With this in place,
type IsPair a = IsF (,) a
type IsMaybe a = IsF Maybe a
...
Tests for the generalization (this stuff only works for GHC 7.10 or later; polykinds is too flaky before that).
data Dict c where
Dict :: c => Dict c
test1 :: Dict (IsF f (f a))
test1 = Dict
test2 :: Dict (IsF f (f a b))
test2 = Dict
test3 :: Dict (IsF f (f a b c))
test3 = Dict
test4 :: Dict (IsF f (f a b c d))
test4 = Dict
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With