(懒惰)Haskell 在 OCaml 中未定义/底部

Bai*_*ker 6 ocaml undefined lazy-evaluation lazy-initialization

Haskell 有一个非常膨胀的undefined值,它懒惰地引发异常(在评估时)。Ocaml 当然是严格的,所以据我所知,没有等同于 Haskell 的undefined. 不过这很不幸,因为这意味着值没有底部类型。假设我想要一个

val a : int
Run Code Online (Sandbox Code Playgroud)

我当然可以

let a = failwith "undefined"
let () =
  print_string "something unrelated\n"
Run Code Online (Sandbox Code Playgroud)

这很高兴编译。不幸的是,在运行它时,我们得到了未定义的异常(这是预期的)。

我想要的是在不改变它的类型的情况下让它a成为一个底部/undefined值(这样的事情Lazy就行不通了)。这可能吗?

额外的细节: 所以我所要求的可能听起来很愚蠢。为了减少对我为什么不应该这样做的任何评论,请允许我简要描述我的用例。我正在编写一个脚本来修改mli文件的 AST以生成一个ml与其签名匹配的“空”文件。对于一般情况,您可能有val a : intmli所以我需要一种方法来一般地合成底部类型。failwith "undefined"如果我只需要编译成功就可以工作。但不幸的是,我还需要将此ml文件链接到 OUnit 测试套件并运行它(显然该套件会失败,但目的是能够运行它,-list-test以便我可以以编程方式获取所有测试的列表)。

更多细节: 我认识到解决这个问题的正确方法(可能)是编写一个可以为任何泛型类型生成底部类型的函数。对于内置原语(和list,option等),这很简单(只是冗长的)。这对于记录变得更加复杂(可能在 stdlib 中定义,但也可能在同一个文件或不同的包中定义)。为了处理这个问题,我的 AST 转换器需要对 OCaml 类型系统和包的文件导入策略有一个完整的理解,这比我想要/应该包含的逻辑要多得多。

Bai*_*ker 1

重要提示:正如 @PatJ 和 @camlspotter 所指出的,Obj.magic可能会导致一些可怕的行为。事实上,我会冒险使用它返回的东西,而无需某种证明(例如使用 Coq)证明它的使用是有保证的,这类似于 C 中的未定义行为。你可能什么也得不到,你可能会得到段错误,或者你可以放火烧你的房子。一切皆有可能。当您从不使用或调用任何未定义的值时才应使用以下解决方案因此,这对于我的情况来说很好,因为我只需要使用未定义的值将 OUnit 测试套件链接到此包(但 OUint从不调用包中的任何内容)。这允许您运行 OUint 测试套件-list-testfailwith在 的情况下运行测试二进制文件会立即失败let a = failwith "undefined")。但由于-list-test从不运行其链接的任何代码,因此它从不尝试使用未定义的值,因此这个特定用例是安全的。谨慎行事。

我设法找到了一个相当老套的解决方案。该Obj模块支持一些奇特的运行时事物,需要一些松散类型(并且似乎不进行运行时检查)。它提供了一些带有一些有前途的签名的功能:

type t 
val repr : 'a -> t
val obj : t -> 'a
val magic : 'a -> 'b
Run Code Online (Sandbox Code Playgroud)

经过一些实验,很明显这些是不安全的操作(它们不进行任何运行时检查),这对于我们的用例来说是完美的(因为我们不太关心如果您尝试使用这些被删除的值会发生什么) 。其中任何一个似乎都足以相当于 Haskell 的undefined

Obj.obj (Obj.repr 0) (* 0 could be unit, another primitive, etc. *)
Obj.magic 0
Run Code Online (Sandbox Code Playgroud)

当然,当您尝试在任何地方使用这些值时,等效性就结束了。您可能会遇到一些段错误。对于我前面提到的用例来说,这很好,因为 OUnit-list-test从未实际运行测试(如果运行了,它们也会在单独的进程中运行,因此段错误不会影响测试工具)。

我很想知道是否有对 OCaml 内部结构有更多经验的人可以对这种方法发表评论。如果使用这些值导致异常而不是潜在的段错误(或其他一些未定义的行为 - 有时似乎0什么也没发生),那将非常酷(尽管在这种情况下并不是真正需要)。

  • 据我所知,在某些情况下可能需要 Obj。例如,当 Coq 生成 ocaml 时,他有时会使用它 [1],特别是第 23.3 节。[1] https://coq.inria.fr/distrib/current/refman/extraction.html (2认同)