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:
Not (Sweet Lemon)
proof, assuming there're many sweet alternatives in the Fruit
definition.So, this way doesn't quite seem satisfying, almost impractical.
Are there more elegant approaches?
@slcv is right: using a function that computes whether a fruit satisfies a property or not rather than building various inductive predicates will allow you to get rid of this overhead.
Here is the minimal setup:
data Is : (p : a -> Bool) -> a -> Type where
Indeed : p x = True -> Is p x
isnt : {auto eqF : p a = False} -> Is p a -> b
isnt {eqF} (Indeed eqT) = case trans (sym eqF) eqT of
Refl impossible
Is p x
ensures that the property p
holds of the element x
(I used an inductive type rather than a type alias so that Idris doesn't unfold it in the context of a hole; it's easier to read this way).
isnt prf
dismisses the phoney proof prf
whenever the typechecker is able to generate a proof that p a = False
automatically (via Refl
or an assumption in the context).
Once you have these, you can start defining your properties by only enumerating the positive cases and adding a catchall
wineFruit : Fruit -> Bool
wineFruit Grape = True
wineFruit Apple = True
wineFruit _ = False
weaponFruit : Fruit -> Bool
weaponFruit Apple = True
weaponFruit Orange = True
weaponFruit Lemon = True
weaponFruit _ = False
You can define your original predicates as type aliases calling Is
with the appropriate decision function:
WineStock : Fruit -> Type
WineStock = Is wineFruit
And, sure enough, isnt
allows you to dismiss the impossible cases:
dismiss : WineStock Orange -> Void
dismiss = isnt
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