记录中的F#协方差和逆变

时间:2016-04-18 08:46:14

标签: f#

我需要描述以下关系:

type FieldDef<'Object, 'Val> { 
    Name: string; 
    Resolve: 'Object -> 'Val 
}

type ObjectDef<'Object> { 
    Name: string; 
    Fields: FieldDef<'Object, _> list 
}

// example usage
type MyRecord = { X: int; Y: string; }

let myRecordDef = {
    Name = "MyRecord"
    Fields = [
        { Name = "x"; Resolve: fun r -> r.X }
        { Name = "y"; Resolve: fun r -> r.Y }
    ]
}

不幸的是,_在此用例中无效。在这种情况下,FieldDef的第二种类型参数可能会有所不同 - 在C#中,我们可以将其注释为逆变并绑定到类型Object。如何在不失去太多类型安全的情况下在F#中实现类似的结果?

1 个答案:

答案 0 :(得分:2)

正如约翰所说,如果你想要完全通用,那么你就会失去类型安全性。那么你的ObjectDef类是

type ObjectDef<'Object> =
    { 
        Name: string; 
        Fields: FieldDef<'Object, obj> list 
    }

我不知道&#34;如何通用&#34;你需要。这里提供了一个建议,您至少使用一些明确表示的字段类型:首先定义一个有区别的联合,替换完全通用的'Object -> 'Val

type FieldGetter<'T> = 
    | Int of ('T -> int)
    | String of ('T -> string)
    | AnythingElse of ('T -> obj)

然后定义一个新的字段定义记录,以及一些方便的重载:

type FieldDef2<'Object> =
    { 
        Name: string; 
        Resolve: FieldGetter<'Object>
    }
    static member Create(name, f) = { Name = name; Resolve = Int f }
    static member Create(name, f) = { Name = name; Resolve = String f }
    static member Create(name, f) = { Name = name; Resolve = AnythingElse f }
type ObjectDef2<'Object> =
    { 
        Name: string; 
        Fields: FieldDef2<'Object> list 
    }

然后你的例子变成:

let myRecordDef = {
    Name = "MyRecord"
    Fields = [ FieldDef2<_>.Create("x", fun r -> r.X); 
                FieldDef2<_>.Create("y", fun r -> r.Y) ]
}