模拟F#中的'Any'类型

时间:2016-10-06 14:54:32

标签: f#

我想创建一个类似于以下定义的类型:

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的函数调用超载,但我无法让它工作。有没有人在尝试之前/有任何想法?

1 个答案:

答案 0 :(得分:10)

在这种情况下,价值限制不是问题,如果您希望在一般情况下进一步使用它们,则需要makeLeftmakeRight的结果是通用的。

在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