我正在尝试编写一个可选择将函数作为参数的函数
let xxx ?(extractor = (fun a -> a)) yyy = ...
这最终会有类型:
val xxx: ?extractor:('a -> 'a) -> 'c -> ...
我的目的是让提取器成为一个从结构中提取信息的函数,所以返回类型可以是任何东西,但我希望默认为身份函数
我试图在mli中更改它的签名
val xxx: ?extractor:('a -> 'b) -> 'c -> ...
但它没有编译,说('a - >'a)与(a' - >'b)不兼容。我觉得很奇怪('a - >'a)不是(a' - >'b)的子集。是否有一种语法可以放在mli文件中说('a - > *)?
答案 0 :(得分:3)
在函数式语言中,从函数返回的未指定(变量)类型基本上必须出现在您传入的类型中的某个位置。否则,值将来自何处?所以没有'a -> 'b.
考虑您期望传递给函数的类型以及提取器函数如何与它们相关可能会有所帮助。应该可以有一个相当直接的关系。与类型系统进行战斗通常意味着你正在尝试做一些可能意外失败的事情,这就是类型系统试图阻止的事情。
修改强>
也许我想说的是'a
和'b
,提取器函数的输入和输出类型,不会是任意类型。输入类型'a
可能以某种方式与类型'c
相关。类似地,结果类型'b
可能与函数xxx
的返回类型整体相关。如果没有关于这些关系的更多信息,很难提出建议。
如果你试图将它们视为独立的任意类型,那么你会遇到我上面讨论的参数不可能性。 (它还需要高级打字,我认为它是2级多态。)
举个简单的例子,假设'a
和'c
是相同的类型,'b
是函数的返回类型。那么你的函数几乎必须看起来像这样:
let xxx ?extractor s =
match extractor with
| Some f -> f s
| None -> s
这具有您想要的行为:默认提取器功能是标识功能。但这也会强制f
的返回类型与其输入类型相同。所以xxx的类型是:
val xxx : ?extractor:('a -> 'a) -> 'a -> 'a
除非你想变得非常花哨,否则我真的没办法解决这个问题,而且我怀疑一个奇特的解决方案会带来复杂性,这会超过使用可选参数的便利性。
也许它可以有两个功能。要继续简化示例,它们将如下所示:
# let xxx extractor s = extractor s;;
val xxx : ('a -> 'b) -> 'a -> 'b = <fun>
# let xxx_id s = xxx (fun x -> x) s;;
val xxx_id : 'a -> 'a = <fun>
我认为这些都有你想要的类型,唯一的不便是你有两个不同的名字。
答案 1 :(得分:3)
当函数签名具有类型变量如'a
,'b
等时,这并不意味着该函数可以在运行时将所需的任何类型放在其中。相反,这意味着来自外部的人可以告诉函数每个类型变量的类型,无论它说什么,函数仍然能够正常工作。 (有人是类型检查器,它根据使用函数的上下文设置类型。)
你所拥有的东西没有意义的原因是,假设该函数用于需要'a
为int
且'b
为{{1}的上下文中}}。您的默认参数string
无法满足所需的类型fun a -> a
。无论你放置什么,都必须适用于任何类型变量的组合,但正如其他人所提到的,拥有一个接受任何类型并返回任何其他所需类型的函数并没有多大意义。
我认为你想要的是两个独立的函数,一个带有额外的参数,另一个没有,后者使用int -> string
作为额外的参数调用前者。请注意,没有额外参数的函数在其类型中将只有一个较少的类型变量,因为它将不再单独fun a -> a
和'a
。
答案 2 :(得分:2)
我的目的是让提取器成为一个提取的函数 来自结构的信息,所以返回类型可以是任何东西,但是 我希望默认为身份功能
O_PONIES?归类函数的返回类型不能是任何,而是某种传递给它。
我觉得很奇怪('a - &gt;'a)不是(a' - &gt;'b)的子集。
如果你想用类型B约束类型A,那么类型B的每个实例都应该是类型A的实例,即类型B应该是类型A的子集(换句话说,比它更窄)(不是另一种方式)回合)。在你的情况下('a - &gt;'b)不是('a - &gt;'a)的子集。这与LSP有关,但直觉就足以解决这个问题。