Thanks for this suggestion, David. It seems to work out well, though I haven't tried running yet.
> unsafeDict :: Dict c
> unsafeDict = unsafeCoerce (Dict @ ())
>
> unsafeSatisfy :: forall c a. (c => a) -> a
> unsafeSatisfy z | Dict <- unsafeDict @ c = z
This doesn't really smell right to me, no. Dict @() is actually a rather different value than you seek. In general, these look like they do way more than they ever can. I would suggest you look through those comparison *constraints* to the underlying type equalities involving the primitive CmpNat type family.
-- Better, because there's only one Refl
unsafeEqual :: forall a b. a :~: b
unsafeEqual :: unsafeCoerce Refl
unsafeWithEqual :: forall a b r. (a ~ b => r) -> r
unsafeWithEqual r
| Refl <- unsafeEqual @a @b = r
compareEv = case .... of
LT -> unsafeWithEqual @(CmpNat u v) @LT CompareLT
...
Now we can define `compareEv`:
> compareEv :: forall u v. KnownNat2 u v => CompareEv u v
> compareEv = case natValAt @ u `compare` natValAt @ v of
> LT -> unsafeSatisfy @ (u < v) CompareLT
> EQ -> unsafeSatisfy @ (u ~ v) CompareEQ
> GT -> unsafeSatisfy @ (u > v) CompareGT
If anyone has other techniques to suggest, I'd love to hear.
-- Conal