GADT模式匹配
GADT pattern matching
我最近一直在玩 GADT,想知道是否有人可以为我指明正确的方向,让我学习如何输入它以便编译,如果可能的话,或者我是否让事情过于复杂。
我在这里看到了一些关于 GADT 模式匹配的其他答案,但这似乎有点不同。
我见过这种表示没有可能值的类型的事情:
module Nothing = {
type t =
| Nothing(t);
};
所以我想用它来锁定这个 Exit.t 类型,这样我就可以有一个 Exit.t('a, Nothing.t) 的类型来表示成功案例,捕获事实上没有可恢复的失败值。
module Exit = {
type t('a, 'e) =
| Success('a): t('a, Nothing.t)
| Failure('e): t(Nothing.t, 'e);
这似乎没问题,直到我尝试为它编写一个 flatMap 函数。
let flatMap: ('a => t('a1, 'e), t('a, 'e)) => t('a1, 'e) = (f, exit) =>
switch (exit) {
| Success(a) => f(a)
| Failure(_) as e => e
};
};
照原样,它推断类型 Exit.t 总是 Exit.t(Nothing.t, Nothing.t) 我有点理解,因为失败中的类型案例会将第一种类型设置为 Nothing,成功案例会将第二种类型设置为 Nothing。
我尝试了我所知道的一件事,使用 type a
使其中一些类型成为本地类型。我已经尝试 type a a1 e
和 type a e
离开 'a1
但我似乎无法理解这个想法。
(我在下面使用 OCaml 语法,因为这个问题也被标记为“ocaml”,但对于 Reason 来说也是如此。)
首先,您的类型 Nothing.t
不为空。循环值 Nothing (Nothing (Nothing (...)))
是一个有效的居民。如果你真的想让类型为空,就不要放任何构造函数。
其次,正如您所猜测的,在 flat_map
中,您的 Failure
分支强制 'a1
用 Nothing.t
实例化。没有办法解决这个问题;这不是编译器的缺陷,只是解释这段代码的唯一方法。
第三,你把事情弄得太复杂了,因为 Exit.t
不必首先成为 GADT 就可以实现你的目标。
下面是一个更简单的示例,表明如果 Nothing.t
实际上为空,则编译器正确地允许不相关的分支。
type nothing = |
type ('a, 'b) result =
| Success of 'a
| Failure of 'b
let only_success (x : ('a, nothing) result) : 'a =
match x with
| Success v -> v
| Failure _ -> . (* this branch can be removed, as it is correctly inferred *)
我最近一直在玩 GADT,想知道是否有人可以为我指明正确的方向,让我学习如何输入它以便编译,如果可能的话,或者我是否让事情过于复杂。
我在这里看到了一些关于 GADT 模式匹配的其他答案,但这似乎有点不同。
我见过这种表示没有可能值的类型的事情:
module Nothing = {
type t =
| Nothing(t);
};
所以我想用它来锁定这个 Exit.t 类型,这样我就可以有一个 Exit.t('a, Nothing.t) 的类型来表示成功案例,捕获事实上没有可恢复的失败值。
module Exit = {
type t('a, 'e) =
| Success('a): t('a, Nothing.t)
| Failure('e): t(Nothing.t, 'e);
这似乎没问题,直到我尝试为它编写一个 flatMap 函数。
let flatMap: ('a => t('a1, 'e), t('a, 'e)) => t('a1, 'e) = (f, exit) =>
switch (exit) {
| Success(a) => f(a)
| Failure(_) as e => e
};
};
照原样,它推断类型 Exit.t 总是 Exit.t(Nothing.t, Nothing.t) 我有点理解,因为失败中的类型案例会将第一种类型设置为 Nothing,成功案例会将第二种类型设置为 Nothing。
我尝试了我所知道的一件事,使用 type a
使其中一些类型成为本地类型。我已经尝试 type a a1 e
和 type a e
离开 'a1
但我似乎无法理解这个想法。
(我在下面使用 OCaml 语法,因为这个问题也被标记为“ocaml”,但对于 Reason 来说也是如此。)
首先,您的类型 Nothing.t
不为空。循环值 Nothing (Nothing (Nothing (...)))
是一个有效的居民。如果你真的想让类型为空,就不要放任何构造函数。
其次,正如您所猜测的,在 flat_map
中,您的 Failure
分支强制 'a1
用 Nothing.t
实例化。没有办法解决这个问题;这不是编译器的缺陷,只是解释这段代码的唯一方法。
第三,你把事情弄得太复杂了,因为 Exit.t
不必首先成为 GADT 就可以实现你的目标。
下面是一个更简单的示例,表明如果 Nothing.t
实际上为空,则编译器正确地允许不相关的分支。
type nothing = |
type ('a, 'b) result =
| Success of 'a
| Failure of 'b
let only_success (x : ('a, nothing) result) : 'a =
match x with
| Success v -> v
| Failure _ -> . (* this branch can be removed, as it is correctly inferred *)