是否有可能以这样的方式设计我的类型:
let fieldValues = [nameField, VText "string"; ageField, VInteger 13]
但不是这样:(从某种意义上说它将是一个编译时错误):
let fieldValues = [nameField, VInteger 13; ageField, VText "string"]
type value =
| VText of string
| VInteger of int
type ty =
| TText
| TInteger
type field = { Id: int; Type: ty; Name: string }
let nameField = { Id=1; Type=TText; Name="Name" }
let ageField = { Id=2; Type=TInteger; Name="Age" }
答案 0 :(得分:2)
列表中的元组属于value * ty
类型。为了让编译器注意到你需要连接两个,你需要让编译器知道'你需要独特的,连接的,状态。这可能需要您删除一些通用:
type DataType =
| TextData of VText * { Id : int; Type : TText; Name : string }
| IntData of VInteger * { Id : int; Type : TInteger; Name : string }
然后,您将创建一个DataType
列表,如果您尝试将VInteger混合到TText记录中,编译器会注意到这一点。因为您已经在一个有区别的联合中明确说明了这些组合。 value
DI会有点多余:
type DataType =
| TextData of string * { Id : int; Type : string; Name : string }
| IntData of int * { Id : int; Type : int; Name : string }
编辑:(我在一个在电话上打字的酒吧)你可以用通用的方式清理它:
type DataType<'a> = {
Content : 'a * { Id : int; Type : 'a; Name : string }
}
type PossibleType = DataType<int> | DataType<string>
这可能不是理想的方法(其他人会更好);但是,我在这里遵循的原则是编译器只能注意到它之间的关系。很明显,这个解决方案对于TypeA -> ValA
关系来说只是相对干净,如果你有很多可能的组合,那么它会变得数字丑陋(此时你需要重新设计DI作为所有可能性的树木或者将变体数据重构为单独的记录。)
答案 1 :(得分:0)
不可能完全按照自己的意愿行事。但是,这里有类似的功能:
type TypedField<'a> = { id : int; name : string }
type FieldConverter<'t> =
abstract Convert : TypedField<'a> * 'a -> 't
// Necessary only because F# type system won't let you implement FieldConverter<unit>
type FieldFunc =
abstract Use : TypedField<'a> * 'a -> unit
type Field =
abstract ApplyConverter : FieldConverter<'t> -> 't
abstract ApplyFunc : FieldFunc -> unit
let mkField field value =
{ new Field with
member __.ApplyConverter(f) = f.Convert(field, value)
member __.ApplyFunc(f) = f.Use(field, value) }
let nameField : TypedField<string> = { id = 1; name = "Name" }
let ageField : TypedField<int> = { id = 2; name = "Age" }
let fields = [mkField nameField "string"; mkField ageField 13]
// won't compile
let fields' = [mkField nameField 13; mkField ageField "string"]
不幸的是,使用这些字段需要相当多的样板:
// print names and values of all fields
fields
|> List.iter (fun f -> f.ApplyFunc { new FieldFunc with member __.Use(field, value) = printfn "%s: %O" field.name value })