我来自C++,习惯了能够构建简单形式的编译时断言,通过使用模板元编程和/或cpp(1)
,在编译期间如果一些简单条件(例如在简单的代数表达式上)没有得到满足,我可以发出警告或错误。
例如,如果我想确保我的程序只在Int
具有至少某个minBound
/maxBound
范围时进行编译,或者在当前编译目标下可以进行无损(可逆)从Int64
到Int
的转换。这是否可以通过一些GHC Haskell扩展来实现?我的第一个猜测是使用TH。还有其他可以用于此目的的GHC设施吗?
我来自C++,习惯了能够构建简单形式的编译时断言,通过使用模板元编程和/或cpp(1)
,在编译期间如果一些简单条件(例如在简单的代数表达式上)没有得到满足,我可以发出警告或错误。
例如,如果我想确保我的程序只在Int
具有至少某个minBound
/maxBound
范围时进行编译,或者在当前编译目标下可以进行无损(可逆)从Int64
到Int
的转换。这是否可以通过一些GHC Haskell扩展来实现?我的第一个猜测是使用TH。还有其他可以用于此目的的GHC设施吗?
这是一个广义且稍微简化的版本,基于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 调用来说,这不是可选的吗? - hvr使用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)
Int64
在编译时是否适合于Int
吗? - hvrlo
和hi
是整数的边界时,可以使用Int lo hi
类型。例如,(+)
现在的类型为Int l1 h1 -> Int l2 h2 -> Int (l1 + l2) (h1 + h2)
。 - Alexandre C.minBound/maxBound
检查,您也可以使用configure
脚本。 - Mikhail Glushenkov-XCPP
标识(或{-# LANGUAGE CPP #-}
指令)。 - Antal Spector-Zabusky