模拟F#中的'Any'类型

Mat*_*emp 9 f#

我想创建一个定义类型的类型:

type LeftRight<'left, 'right> = {
    Left : 'left list
    Right : 'right list
    }
Run Code Online (Sandbox Code Playgroud)

和几个功能:

let makeLeft xs = { Left = xs; Right = [] }

let makeRight ys = { Left = []; Right = ys }
Run Code Online (Sandbox Code Playgroud)

我想提供一个'组合器'功能:

let combine l r = { Left = l.Left @ r.Left; Right = l.Right @ r.Right }
Run Code Online (Sandbox Code Playgroud)

当我尝试做某事时,我(显然!)因为我的价值是通用的而得到问题:

let aaa = makeLeft [1;2;3]
// Value restriction. The value 'aaa' has been inferred to have generic type
// val aaa : LeftRight<int,'_a>    
Run Code Online (Sandbox Code Playgroud)

如果我合并左侧和右侧,类型推断就会开始,一切都是A-OK:

let bbb = makeRight [1.0;2.0;3.0]
let comb = combine aaa bbb // LeftRight<int, float>
Run Code Online (Sandbox Code Playgroud)

但是我想用一个只有lefts的东西.我尝试创建一个'Any'类型:

type Any = Any
Run Code Online (Sandbox Code Playgroud)

并在makeLeft和makeRight上明确指定了类型:

let makeLeft xs : LeftRight<_, Any> = { Left = xs; Right = [] }

let makeRight ys : LeftRight<Any, _> = { Left = []; Right = ys }
Run Code Online (Sandbox Code Playgroud)

这使得值定义变得愉快,但使组合函数变得悲伤:

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'
Run Code Online (Sandbox Code Playgroud)

我觉得可能有一种解决这个问题的方法,因为.Net的函数调用超载了伏都教,但是我无法让它工作.有没有人尝过这个/有任何想法?

The*_*ght 10

在这种情况下,价值限制不是问题,如果您希望一般地使用它们,那么您需要结果makeLeft或是makeRight通用的.

在F#(和OCaml)中,必须使用完整类型注释明确标记泛型语法值.实际上,编译器报告了这个:

错误FS0030:值限制.值'aaa'被推断为具有泛型类型val aaa:LeftRight将'aaa'定义为简单数据项,使其成为具有显式参数的函数,或者,如果您不打算将其作为通用类型,则添加类型注解.

没有太多细节*,这是为了避免在结合多态性和副作用时可能出现的问题.缺点是它确实拒绝了一些非常安全的代码.

所以,解决方案很简单,我们使这些值明确通用:

let aaa<'a> : LeftRight<int,'a> = makeLeft [1;2;3]

let bbb<'a> : LeftRight<'a, float> = makeRight [1.0;2.0;3.0]
Run Code Online (Sandbox Code Playgroud)

将它们放在FSI中:

let comb = combine aaa bbb;;;
Run Code Online (Sandbox Code Playgroud)
val comb : LeftRight<int,float> = {Left = [1; 2; 3];
                               Right = [1.0; 2.0; 3.0];}
Run Code Online (Sandbox Code Playgroud)

请注意,如果你把没有中间let绑定,你不再有一个通用的价值和正确的类型可以通过编译器可以推断:

combine (makeLeft [1;2;3]) (makeRight [1.0;2.0;3.0]);;
Run Code Online (Sandbox Code Playgroud)
val it : LeftRight<int,float> = {Left = [1; 2; 3];
                                 Right = [1.0; 2.0; 3.0];}
Run Code Online (Sandbox Code Playgroud)

*有关更多详细信息,请查看此文章.