为什么 IIFE 中的包装函数会导致弱类型?



我试图找出一种方法来对模块的用户隐藏某些辅助函数和相关内容,并认为使用 IIFE 会起作用,但它失败了,因为类型变量无法泛化?

我想我已经使用以下代码将其归结为最基本的场景:

module TestA = {
let y = 0;
let x = (type a, numbers: list(a)): option(a) => None;
};
module TestB = {
let x =
(
() => {
let y = 0;
(type a, numbers: list(a)): option(a) => None;
}
)();
};

在 TestB 中,编译器抱怨

41 │ };
42 │ 
43 │ module TestB = {
44 │   let x =
. │ ...
50 │     )();
51 │ };
52 │ 
53 │ module Number = {
The type of this module contains type variables that cannot be generalized:
{ let x: list('_a) => option('_a); }
This happens when the type system senses there's a mutation/side-effect,
in combination with a polymorphic value.
Using or annotating that value usually solves it. More info:
https://realworldocaml.org/v1/en/html/imperative-programming-1.html#side-effects-and-weak-polymorphism

为什么?我该如何处理对模块用户隐藏y的问题?

PS:当重新格式化返回类型注释时,TestB被放在None后面,如下所示:(type a, numbers: list(a)) => (None: option(a)).为什么在这里而不是在模块TestA中?据我了解,这只是"标记"返回的值,所以我在这里看不到区别?

你遇到了所谓的值限制。简而言之,编译器认为闭包内部可能会发生潜在的副作用,这可能会不安全地更改函数的返回类型。因此,它无法安全地决定返回类型。

幸运的是,ReasonML 有一个简单、惯用的 IIFE 替代品——大括号分隔的作用域:

module TestA = {
let x = {
let y = 0;
numbers => None;
};
};

此范围将y隐藏在x的定义中。

隐藏模块中项的更通用方法是为模块提供一个签名,该签名根本不列出隐藏的项。在这种情况下,它看起来像:

module TestA: {
let x: list('a) => option('a);
} = {
let y = 0;
let x = numbers => None;
};

另一种方法是使用"接口文件",例如:

// TestA.rei
let x: list('a) => option('a);
// TestA.re
let y = 0;
let x = numbers => None;

相关内容

  • 没有找到相关文章

最新更新