为啥 iife 中的包装函数会导致弱类型?

Posted

技术标签:

【中文标题】为啥 iife 中的包装函数会导致弱类型?【英文标题】:why does wrapping functions in iife cause weak types?为什么 iife 中的包装函数会导致弱类型? 【发布时间】:2020-02-15 23:39:39 【问题描述】:

我正在尝试找出一种方法来向模块的用户隐藏某些辅助函数和相关内容,并认为使用 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 的问题?

P.s.:当重新格式化TestB 中的返回类型注释时,会像这样放在None 后面:(type a, numbers: list(a)) => (None: option(a))。为什么在这里而不是在模块TestA 中?据我了解,这只是“标记”返回的值,所以我在这里看不出有什么区别?

【问题讨论】:

【参考方案1】:

您点击了所谓的value restriction。简而言之,编译器认为闭包内部可能会发生潜在的副作用,可能会不安全地更改函数的返回类型。所以它不能安全地决定返回类型。

幸运的是,ReasonML 有一个简单、惯用的 IIFE 替代品–brace-delimited scopes:

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;

【讨论】:

以上是关于为啥 iife 中的包装函数会导致弱类型?的主要内容,如果未能解决你的问题,请参考以下文章

在js中获取页面元素的属性值时,弱类型导致的诡异事件踩坑记录,

为啥说 Python 是强类型语言

强类型弱类型动态类型静态类型语言

弱类型强类型动态类型静态类型语言的区别

存储弱类型变量的方法

弱类型强类型动态类型静态类型语言的区别是什么?