import Data.Void (Void,absurd)
Say I have a small term language:
data Term c v where
Var :: v -> Term c v
Con :: c -> [Term c v] -> Term c v
If I want to combine terms of the type Term c Void
and Term c Int
, that should be possible, since I have the guarantee that the first term does not contain any variables. Therefore, I can write the function:
castVar :: Term c Void -> Term c v
castVar (Var i ) = absurd i
castVar (Con x xs) = Con x (map castVar xs)
However, it'd be a shame to actually run this function, since I know for a fact that it doesn't actually change anything. Is it safe to add the following pragmas:
{-# NOINLINE castVar #-}
{-# RULES "castVar/id" forall x. castVar x = x; #-}
And will this actually achieve the desired result?
Are there better ways of doing this?
No, this wouldn't work. Rewrite rules are only applied if the types check. In this case the rule would only fire if you ran castVar :: Term c Void -> Term c Void
, which isn't very helpful. (See https://downloads.haskell.org/~ghc/7.10.1/docs/html/users_guide/rewrite-rules.html for more info on rewrite rules)
What you want is to coerce the type. It's safe to do this because you know there's no Void
in the Term
. You could either do this by importing Unsafe.Coerce
and castVar = unsafeCoerce
or make Term
an instance of Functor
(you can derive it for this simple version) and use unsafeVacuous
from Data.Void.Unsafe
.