Types 如何使用类型变量在ReasonML中编写函数以接受任何类型的参数?
我注意到ReasonML中的类型推断机制有一个非常奇怪的行为。我有一个包含标识功能的记录。当我直接使用记录实例时,编译器不会抱怨。但当我将记录传递给另一个函数并尝试调用identity函数时,类型推断会抱怨:Types 如何使用类型变量在ReasonML中编写函数以接受任何类型的参数?,types,polymorphism,ocaml,reason,Types,Polymorphism,Ocaml,Reason,我注意到ReasonML中的类型推断机制有一个非常奇怪的行为。我有一个包含标识功能的记录。当我直接使用记录实例时,编译器不会抱怨。但当我将记录传递给另一个函数并尝试调用identity函数时,类型推断会抱怨: type idRecord('a) = { // idFn can take any type. idFn: 'a => 'a }; let myRecord: idRecord('a) = { idFn: anyVal => anyVal }; // WORK
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
我需要什么才能使类型推断愉快地接受任何类型的参数?我对类型推断算法不是专家,但对我来说,它在第一种情况下工作似乎很奇怪,因为类型变量是在记录上定义的,而不仅仅是函数。考虑如果您添加另一个字段到<代码> IDCords>代码> > < < /代码>:
type idRecord('a) = {
idFn: 'a => 'a,
value: 'a
};
我怀疑它之所以有效,是因为类型推断规则的放宽,这种规则只适用于一些非常有限的条件,其中不包括作为函数参数的记录
在任何情况下,解决方案都很简单:从记录中删除类型变量,并在函数类型签名中普遍量化'a
:
type idRecord = {
idFn: 'a. 'a => 'a
};
'a.
,应该读为“for all'a
”,确保'a
是完全多态的,可以接受任何类型。我对类型推断算法不是很精通,但对我来说,它在第一种情况下工作似乎很奇怪,因为类型变量是在记录上定义的,而不仅仅是函数。考虑如果您添加另一个字段到<代码> IDCords>代码> > < < /代码>:
type idRecord('a) = {
idFn: 'a => 'a,
value: 'a
};
我怀疑它之所以有效,是因为类型推断规则的放宽,这种规则只适用于一些非常有限的条件,其中不包括作为函数参数的记录
在任何情况下,解决方案都很简单:从记录中删除类型变量,并在函数类型签名中普遍量化'a
:
type idRecord = {
idFn: 'a. 'a => 'a
};
'a.
,应该读为“对于所有'a
s”,确保'a
是完全多态的,并且可以接受任何类型。根本问题是,您的函数运行程序
是二级多态的,或者换句话说,使用多态函数作为参数有点复杂
更严重的是,在幻想语法中,runProgram
的类型是('a.'a=>'a)=>unit
,其中'a.'a=>'a
表示为任何'a
工作所需的函数。这与类似的函数形成对比
let apply: 'a. ('a -> 'a) -> 'a -> 'a = (f, x) => f(x)
其中,首先引入类型变量'a
(在prenex位置),然后只要求函数参数对该特定类型'a
起作用。比如说
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)
类型检查器为不明确
推断的类型是(int=>int)=>int=>int
。
但是,如果我用带有多态字段的记录替换f
(这是在OCaml中编写高阶多态函数的两种方法之一)
不明确的类型(在幻想语法中)变成('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手册。根本问题是,您的函数运行程序
是二阶多态的,或者换句话说,使用多态函数作为参数有点复杂
更严重的是,在幻想语法中,runProgram
的类型是('a.'a=>'a)=>unit
,其中'a.'a=>'a
表示为任何'a
工作所需的函数。这与类似的函数形成对比
let apply: 'a. ('a -> 'a) -> 'a -> 'a = (f, x) => f(x)
其中,首先引入类型变量'a
(在prenex位置),然后只要求函数参数对该特定类型'a
起作用。比如说
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)
类型检查器为不明确
推断的类型是(int=>int)=>int=>int
。
但是,如果我用带有多态字段的记录替换f
(这是在OCaml中编写高阶多态函数的两种方法之一)
不明确的类型(在幻想语法中)变成('a.'a=>int)=>'a=>int
。换句话说,如果类型推断能够推断出更高级别的多态性,那么它必须在('a.'a=>int)=>'a=>int
和(int=>int)=>int=>int
之间做出决定。没有c