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


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

例如,如果我想确保我的程序只在何时编译 Int 至少有一定的 minBound/maxBound 范围或者,如果是无损失(如可逆)转换 Int64 至 Int 可以使用当前编译目标。这可能是GHC Haskell扩展的一部分吗?我的第一个猜测是使用TH。是否有其他GHC设施可以被利用?


3913
2017-07-11 10:26


起源

(联合国)幸运的是,Haskell中没有依赖类型,这对于此目的而言非常好(但可能需要您添加更多类型注释)。 - Alexandre C.
@Alexandre:听起来很有意思......你能详细说明依赖类型如何用来决定是否 Int64 适合 Int 在编译时? - hvr
@hvr:你可以使用 Int lo hi 输入时 lo 和 hi 是整数的边界。现在,例如。 (+) 有类型 Int l1 h1 -> Int l2 h2 -> Int (l1 + l2) (h1 + h2)。 - Alexandre C.
为了 minBound/maxBound 检查你也可以使用 configure 脚本。 - Mikhail Glushenkov
我不知道你是否可以将它用于此目的,但是GHC允许你使用Haskell代码运行C预处理器 -XCPP 旗帜(或 {-# LANGUAGE CPP #-} 指示)。 - Antal Spector-Zabusky


答案:


这是一个广义的,略微简化的版本 安东尼的榜样

{-# 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")

8
2017-07-11 18:45



这确实是我对TH周围的改进! - Anthony
非常感谢@hammar和@Anthony。所需的TH-voodoo看起来比我想象的要容易: - )...顺便说一下,你为什么要使用 $( .. ) 句法?对于顶级TH调用,这不是可选的吗? - hvr
@hvr:是的,尽管我还是喜欢用它来让TH更容易在心理上将TH与普通代码区分开来。 - hammar
即使它是可选的,它也是 办法 更清楚地统一使用拼接语法。 - Carl
@Carl非常好。 Haskell的宏观文化仍处于初期阶段,因此我们需要轻率地避免残酷地扼杀我们目前拥有的美貌。 - Anthony


使用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)

6
2017-07-11 17:55