Let's say I have
data Fruit = Apple | Banana | Grape | Orange | Lemon | {- many others -}
and a predicate on that type,
data WineStock : Fruit -> Type where
CanonicalWine : WineStock Grape
CiderIsWineToo : WineStock Apple
which doesn't hold for Banana
, Orange
, Lemon
and others.
It can be said that this defines WineStock
as a predicate on Fruit
; WineStock Grape
is true (since we can construct a value/proof of that type: CanonicalWine
) as well as WineStock Apple
, but WineStock Banana
is false, since that type is not inhabited by any values/proofs.
Then, how can I go about using effectively Not (WineStock Banana)
, Not (WineStock Lemon)
, etc? It seems that for each Fruit
constructor besides Grape
and Apple
, I can't help but have to code up a case split over WineStock
, somewhere, full of impossible
s:
instance Uninhabited (WineStock Banana) where
uninhabited CanonicalWine impossible
uninhabited CiderIsWineToo impossible
instance Uninhabited (WineStock Lemon) where
uninhabited CanonicalWine impossible
uninhabited CiderIsWineToo impossible
instance Uninhabited (WineStock Orange) where
uninhabited CanonicalWine impossible
uninhabited CiderIsWineToo impossible
Note that:
- the code is repetitive,
- LoC will explode when the predicate definition grows, gaining more constructors. Just imagine the
Not (Sweet Lemon)
proof, assuming there're many sweet alternatives in theFruit
definition.
So, this way doesn't quite seem satisfying, almost impractical.
Are there more elegant approaches?
data WineFruit = Grape | Apple
and other fruitsdata Fruit = WineFruit WineFruit | Banana | Orange | Lemon
– MonovalentPieFruit
,SaladFruit
,WeaponFruit
, etc. – BilWineStock
? Can't you just defineisWineStock
as a value level function and use it in proofs where appropriate? – Sadoc