我想创建一个类似于以下定义的类型:
type LeftRight<'left, 'right> = {
Left : 'left list
Right : 'right list
}
以及一些功能:
let makeLeft xs = { Left = xs; Right = [] }
let makeRight ys = { Left = []; Right = ys }
我想提供'组合器'功能:
let combine l r = { Left = l.Left @ r.Left; Right = l.Right @ r.Right }
当我尝试制作某些东西时,我(显然!)因为我的价值是通用的而得到问题:
let aaa = makeLeft [1;2;3]
// Value restriction. The value 'aaa' has been inferred to have generic type
// val aaa : LeftRight<int,'_a>
如果我合并左侧和右侧,类型推理就会开始,一切都是A-OK:
let bbb = makeRight [1.0;2.0;3.0]
let comb = combine aaa bbb // LeftRight<int, float>
但我想使用一个只有lefts的人。我尝试创建一个'Any'类型:
type Any = Any
并在makeLeft和makeRight上明确指定了类型:
let makeLeft xs : LeftRight<_, Any> = { Left = xs; Right = [] }
let makeRight ys : LeftRight<Any, _> = { Left = []; Right = ys }
使值定义变得愉快,但使合并函数变得悲伤:
let combined = combine aaa bbb
// Type mismatch. Expecting a
// LeftRight<int,Any>
// but given a
// LeftRight<Any,float>
// The type 'int' does not match the type 'Any'
我觉得可能有一种方法可以解决这个问题,因为.Net的函数调用超载,但我无法让它工作。有没有人在尝试之前/有任何想法?
答案 0 :(得分:10)
在这种情况下,价值限制不是问题,如果您希望在一般情况下进一步使用它们,则需要makeLeft
或makeRight
的结果是通用的。
在F#(和OCaml)中,必须使用完整类型注释明确标记通用语法值。实际上,编译器报告了这一点:
错误FS0030:值限制。价值&#39; aaa&#39;被推断为 有通用类型 val aaa:LeftRight定义&#39; aaa&#39;作为一个简单的数据术语,使其成为具有显式参数的函数,或者如果你这样做 不打算通用,添加类型注释。
没有太多细节*,这是为了避免在组合多态性和副作用时可能出现的问题。缺点是它确实拒绝了一些非常安全的代码。
因此,解决方案很简单,我们将这些值明确地设为通用:
let aaa<'a> : LeftRight<int,'a> = makeLeft [1;2;3]
let bbb<'a> : LeftRight<'a, float> = makeRight [1.0;2.0;3.0]
将它们放在FSI中:
let comb = combine aaa bbb;;;
val comb : LeftRight<int,float> = {Left = [1; 2; 3]; Right = [1.0; 2.0; 3.0];}
请注意,如果在没有中间let绑定的情况下组合,则不再具有通用值,并且编译器可以推断出正确的类型:
combine (makeLeft [1;2;3]) (makeRight [1.0;2.0;3.0]);;
val it : LeftRight<int,float> = {Left = [1; 2; 3]; Right = [1.0; 2.0; 3.0];}
*有关详细信息,请查看this article。