使用 GHC Haskell 进行编译时断言?

13

我来自C++,习惯了能够构建简单形式的编译时断言,通过使用模板元编程和/或cpp(1),在编译期间如果一些简单条件(例如在简单的代数表达式上)没有得到满足,我可以发出警告或错误。

例如,如果我想确保我的程序只在Int具有至少某个minBound/maxBound范围时进行编译,或者在当前编译目标下可以进行无损(可逆)从Int64Int的转换。这是否可以通过一些GHC Haskell扩展来实现?我的第一个猜测是使用TH。还有其他可以用于此目的的GHC设施吗?


3
(不)幸运的是,Haskell 中没有依赖类型,这对于此目的本来很好(但可能需要您放置更多的类型注释)。 - Alexandre C.
@Alexandre:听起来很有趣...你能详细解释一下如何使用依赖类型来确定Int64在编译时是否适合于Int吗? - hvr
依赖类型将完整的定理证明器编码到类型系统中。数据的任何属性都可以在其类型中表达。 - kamatsu
1
@hvr:当lohi是整数的边界时,可以使用Int lo hi类型。例如,(+)现在的类型为Int l1 h1 -> Int l2 h2 -> Int (l1 + l2) (h1 + h2) - Alexandre C.
1
对于 minBound/maxBound 检查,您也可以使用 configure 脚本。 - Mikhail Glushenkov
1
我不知道你是否可以用它来实现这个目的,但 GHC 允许你在 Haskell 代码上使用 C 预处理器。你可以使用 -XCPP 标识(或 {-# LANGUAGE CPP #-} 指令)。 - Antal Spector-Zabusky
2个回答

11

这是一个广义且稍微简化的版本,基于Anthony的例子

{-# LANGUAGE TemplateHaskell #-}
module StaticAssert (staticAssert) where

import Control.Monad (unless)
import Language.Haskell.TH (report)

staticAssert cond mesg = do
    unless cond $ report True $ "Compile time assertion failed: " ++ mesg
    return [] -- No need to make a dummy declaration

使用方法:

{-# LANGUAGE TemplateHaskell #-}
import StaticAssert

$(staticAssert False "Not enough waffles")

这确实是我在尝试TH时的一次改进! - Anthony
感谢你们两位,@hammar 和 @Anthony。所需的 TH-voodoo 看起来比我预期的要简单 :-)... 顺便问一下,为什么要使用 $( .. ) 语法?对于顶层 TH 调用来说,这不是可选的吗? - hvr
@hvr:是的,虽然我喜欢使用它,以便更容易地将TH与普通代码分开。 - hammar
5
即使是可选的,统一使用拼接语法仍然会更加清晰明了。 - Carl
@Carl提出了一个非常好的观点。Haskell的宏文化仍处于萌芽阶段,因此我们需要小心谨慎,以避免破坏我们目前所拥有的美感。 - Anthony

6

使用TH来实现这并不太困难。这是一个模块,它将所需的断言定义为残留声明的一部分:

{-# LANGUAGE TemplateHaskell #-}
module CompileTimeWarning where
import Control.Monad (unless)
import Data.Int (Int64)
import Language.Haskell.TH

assertInt = let test = fromIntegral (maxBound::Int) == (maxBound::Int64)
            in do unless test $ report True "Int is not safe!"
                  n <- newName "assertion"
                  e <- fmap NormalB [|()|]
                  return $ [FunD n [Clause [] e []]]

使用断言需要一个顶层声明,该声明仅用于断言,而不用于其他任何内容:

{-# LANGUAGE TemplateHaskell #-}
import CompileTimeWarning
$(assertInt)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接