0

是否有可能以这样的方式设计我的类型,我可以这样写:

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" }
4

2 回答 2

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 记录等中,编译器会注意到。因为您已在可区分的联合中明确说明了组合。DI会value有点多余:

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 重新设计为所有可能性的树或将变体数据重构为单独的记录),则该解决方案在数值上会变得丑陋。

于 2013-09-27T16:55:14.527 回答
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 })
于 2013-09-27T19:54:52.723 回答