我应该让我的 Haskell 模块默认安全吗?

Should I make my Haskell modules Safe by default?

标记模块有什么缺点吗Safe?它应该是假定的默认值吗?

正如您在 GHC user manual 中所读到的,如果您将模块标记为 Safe,您将被限制为 Haskell 语言的某个 "safe" 子集。

  1. 您只能导入同样标记为 Safe 的模块(排除 unsafeCoerceunsafePerformIO 等函数)。
  2. 您不能使用模板 Haskell。
  3. 您不能在 IO 之外使用 FFI。
  4. 您不能使用 GeneralisedNewtypeDeriving
  5. 您不能为您在模块中定义的类型手动实现 Generic 类型类。
  6. 等等

作为交换,模块的用户获得了一系列保证(引自手册):

  • Referential transparency — The types can be trusted. Any pure function, is guaranteed to be pure. Evaluating them is deterministic and won’t cause any side effects. Functions in the IO monad are still allowed and behave as usual. So, for example, the unsafePerformIO :: IO a -> a function is disallowed in the safe language to enforce this property.
  • Module boundary control — Only symbols that are publicly available through other module export lists can be accessed in the safe language. Values using data constructors not exported by the defining module, cannot be examined or created. As such, if a module M establishes some invariants through careful use of its export list, then code written in the safe language that imports M is guaranteed to respect those invariants.
  • Semantic consistency — For any module that imports a module written in the safe language, expressions that compile both with and without the safe import have the same meaning in both cases. That is, importing a module written in the safe language cannot change the meaning of existing code that isn’t dependent on that module. So, for example, there are some restrictions placed on the use of OverlappingInstances, as these can violate this property.
  • Strict subset — The safe language is strictly a subset of Haskell as implemented by GHC. Any expression that compiles in the safe language has the same meaning as it does when compiled in normal Haskell.

请注意,推断是安全的。如果您的模块未标记 SafeTrustworthyUnsafe,GHC 会将模块的安全性推断为 SafeUnsafe。当你设置 Safe 标志时,如果 GHC 认为模块实际上不安全,它会发出错误。您还可以设置 -Wunsafe,如果模块被推断为不安全,它会发出警告。如果你让它被推断,即使你的依赖项的安全状态发生变化,你的模块也会继续编译。写出来就是向用户承诺安全状态稳定可靠

手册中描述的一个用例是指运行 "untrusted"代码。如果您在您的产品中提供任何类型的扩展点,并且您希望确保这些功能不会被用来攻击您的产品,您可以要求将扩展点的代码标记为 Safe.

您可以标记您的模块 Trustworthy 并且这在实现您的模块时不会以任何方式限制您。您的模块可能会在 Safe 代码中使用,您有责任不违反 Safe 代码应提供的保证。所以这是一个承诺,你是所述模块的作者,给。在编译标记为 Trustworthy 描述的 here 的模块时,您可以使用标志 -fpackage-trust 启用额外检查。

因此,如果您编写普通库并且没有充分的理由关心 Safe Haskell,那么您可能不应该关心。如果您的模块是安全的,那很好并且可以推断。如果不是,那么这可能是有原因的,比如因为您使用了 unsafePerformIO,这也很好。如果你知道你的模块将以需要它在 -XSafe 下编译的方式使用(例如插件,如上所述),你应该这样做。在所有其他情况下,请不要打扰。