用 let 重建 OCaml 模式

Posted

技术标签:

【中文标题】用 let 重建 OCaml 模式【英文标题】:OCaml pattern reconstruction with let 【发布时间】:2014-05-27 03:31:58 【问题描述】:

所以我注意到可以用“let”表达式重构任何模式,即使它是一个函数声明。

它在以下情况下非常有用:

let [c; f; x] = map (fun _ -> scanf " %f" id) (1--3) in ...

但是,这会发出警告,因为该模式并不详尽:确实,它可能会失败。但在我的情况下,这正是模式不匹配时的预期行为(并且我对输入的断言是错误的):语句应该失败(并且失败可能会在堆栈中的某个点被捕获)。

我不喜欢这些警告(而且我不想全局关闭它们!)所以我不得不依靠非常繁琐(而且不安全):

let c, f, x = match (map (fun _ -> scanf " %f" id) (1--3)) with
| [c; f; x] -> c, f, x
| _ -> failwith "wrong assertion"
in ...

有没有办法获得第一个替代方案的简洁语法而没有可怕的警告?或者另一种结构比完整的匹配语句涉及更少的输入?

注意:我认为如果能够指定一个函数只采用 sum 类型的给定替代项,而不在声明站点发出警告,那也很好。调用者需要确保他使用正确的参数,否则他会收到警告......

type ast = Id of string | Var of string * int
let foo (Var(s,n)) = ...

foo (Var("bar", 42)) (* ok *)
foo (Id "bar")       (* warning; or even error *)

(这是因为对我来说,像“foo s n”这样的签名并不能清楚地表明 foo 旨在与 Var-constructed ast 的数据一起使用。)

【问题讨论】:

你可以定义let foo (s, n) = ...,如果可能的话,由调用者从ast获取(s, n) 是的,但这并不能解决问题。例如,如果另一个替代(甚至类型)具有相同的签名怎么办? 您可能希望有选择地关闭警告,而不是任何类型的类型级别保证。最好的办法是编写一个语法扩展,将let 派生形式转换为与failwith 分支匹配的模式。 谢谢,这听起来更像是我想要实现的目标。 【参考方案1】:

您可以使用变体类型:

type ast = [`Id of string | `Var of string * int]

let foo (`Var (s, n)) = (s, n)
(* val foo : [< `Var of 'a * 'b ] -> 'a * 'b *)

foo (`Var("bar", 42)) (* : string * int = ("bar", 42) *)
foo (`Id "bar") (* Error: This expression has type [> `Id of string ] *)

【讨论】:

是的,但很遗憾不能调用 foo,因为您使用带有 sum 类型对象的变体来定义它:/ @LP_ 你要问的是真的不可能,没有依赖类型。你能得到的最接近的是使用变体类型——但是 OCaml 类型系统限制了你可以使用它们的程度。 Var ("foo", 42) 的类型确实是ast,OCaml 类型检查器中没有静态分析敢质疑它。

以上是关于用 let 重建 OCaml 模式的主要内容,如果未能解决你的问题,请参考以下文章

OCaml 中的模式匹配和嵌套模式匹配

“let () =”在 Ocaml 中是啥意思?

在 OCaml 中分隔多个“let”声明,后跟一个“let in”表达式

Ocaml模式匹配“方形”元组?

Lisp 的 let* 的 Ocaml 等价物?

在 OCaml 中的 let 命令(即 let _ = ... in)中使用下划线通配符有啥副作用吗?