代码之家  ›  专栏  ›  技术社区  ›  Harshal Patil

如何使用类型变量在ReasonML中编写函数以接受任何类型的参数?

  •  0
  • Harshal Patil  · 技术社区  · 7 年前

    我注意到ReasonML中的类型推断机制有一个非常奇怪的行为。我有一个包含标识功能的记录。当我直接使用记录实例时,编译器不会抱怨。但当我将记录传递给另一个函数并尝试调用identity函数时,类型推断会抱怨:

    type idRecord('a) = {
      // idFn can take any type.
      idFn: 'a => 'a
    };
    
    let myRecord: idRecord('a) = {
      idFn: anyVal => anyVal
    };
    
    // WORKS ABSOLUTELY FINE
    let x1 = myRecord.idFn(10);
    let x2 = myRecord.idFn("Something");
    
    let runProgram = (program: idRecord('a)) => {
    
      let _y1 = program.idFn(10);
    
      // BOOM: ERROR
      // This expression has type string but an expression was expected of type int
      let _y2 = program.idFn("Something");
    }
    
    runProgram(myRecord);
    

    错误是:

    此表达式的类型为string,但表达式的类型应为int

    我需要什么才能使类型推断愉快地接受任何类型的论点?

    2 回复  |  直到 7 年前
        1
  •  5
  •   octachron    7 年前

    根本问题是您的功能 runProgram 是二阶多态的,或者换句话说,使用多态函数作为参数有点复杂。

    更严重的是,在幻想语法中 运行程序 ('a. 'a => 'a)=> unit 哪里 'a. 'a => 'a 表示为任何应用程序工作所需的函数 'a

    let apply: 'a. ('a -> 'a) -> 'a -> 'a = (f, x) => f(x)
    

    其中类型变量 ”“是的 ”“是的 . 例如

     let two = apply( (x)=> 1 + x, 1)
    

    即使 (x)=> 1 + x

     let fail = runProgram((x) => 1 + x)
    

    失败是因为 (x) => 1 + x

    回到您的类型推断问题,typechecker未能推断出您心目中的类型的原因是,类型推断和较高秩多态性没有很好地匹配(更准确地说,在存在较高秩多态性的情况下,类型推断是不可判定的)。为了理解为什么,考虑这个简单的函数

    let ambiguous(f,x) = f(1)+f(x)
    

    ambiguous (int=>int)=>int=>int . f 带有多态字段的记录(这是在OCaml中编写高阶多态函数的两种方法之一)

    type const = {f:'a. 'a => int}
    let ambiguous({f},x) = f(1)+f(x)
    

    类型 (在幻想语法中)变成 ('a.'a=>int)=>'a=>int . 换句话说,如果类型推断能够推断出更高级别的多态性,那么就必须在 ('a.'a=>int)=>'a=>int (int=>int)=>int=>int . 这两种类型之间没有明显的赢家:第一种类型对其第一个参数有很强的约束,而对其第二个参数则较为宽松,而第二种类型正好相反。这是一个具有较高等级多态性的一般性问题:有很多潜在的选择,但没有明显的最佳选择。

    这就是为什么typecheker在编写高阶多态函数时需要非常明确:

    type program = { program: 'a. 'a => 'a }
    let runProgram = ({program}) => {
      let _y1 = program(10);
      let _y2 = program("Something");
    }
    

    另请参见位于的OCaml手册 http://caml.inria.fr/pub/docs/manual-ocaml/polymorphism.html#sec61

        2
  •  1
  •   glennsl Namudon'tdie    7 年前

    idRecord 类型 'a

    type idRecord('a) = {
      idFn: 'a => 'a,
      value: 'a
    };
    

    我怀疑它之所以有效,是因为类型推断规则的放宽,这种规则只适用于一些非常有限的条件,其中不包括作为函数参数的记录。

    在任何情况下,解决方案都很简单:从记录中删除类型变量并进行通用量化 ”“是的 在函数类型签名中:

    type idRecord = {
      idFn: 'a. 'a => 'a
    };
    

    'a. ”“是的 ”“是的 是完全多态的,可以接受任何类型。