import Data.Void (Void,absurd)
说我有一个小学期的语言:
data Term c v where
Var :: v -> Term c v
Con :: c -> [Term c v] -> Term c v
如果我想组合Term c Void
和Term c Int
类型的术语,那应该是可能的,因为我保证第一个术语不包含任何变量。因此,我可以编写函数:
castVar :: Term c Void -> Term c v
castVar (Var i ) = absurd i
castVar (Con x xs) = Con x (map castVar xs)
然而,实际运行此功能真是太遗憾了,因为我知道它实际上并没有改变任何东西。添加以下编译指示是否安全:
{-# NOINLINE castVar #-}
{-# RULES "castVar/id" forall x. castVar x = x; #-}
这会实际达到预期效果吗?
有更好的方法吗?
答案 0 :(得分:7)
不,这不行。只有在检查类型时才会应用重写规则。在这种情况下,规则只会在您运行castVar :: Term c Void -> Term c Void
时触发,这不是很有用。 (有关重写规则的更多信息,请参阅https://downloads.haskell.org/~ghc/7.10.1/docs/html/users_guide/rewrite-rules.html)
你想要的是强迫这种类型。这样做是安全的,因为您知道Void
中没有Term
。您可以通过导入Unsafe.Coerce
和castVar = unsafeCoerce
或将Term
设为Functor
的实例来实现此目的(您可以为此简单版本派生它)并使用unsafeVacuous
来自Data.Void.Unsafe
。