F# 如何使用类型系统防止无意义的表达

F# 如何使用类型系统防止无意义的表达,f#,F#,有没有可能设计我的字体,这样我就可以写下: let fieldValues = [nameField, VText "string"; ageField, VInteger 13] 但不是这样:(在某种意义上,这将是一个编译时错误): 列表中的元组类型为value*ty。为了让编译器注意到您需要两个连接的状态,您需要让编译器“知道”您需要不同的、连接的状态。这可能需要您删除一些通用的属性: type DataType = | TextData of VText * { Id : int;

有没有可能设计我的字体,这样我就可以写下:

let fieldValues = [nameField, VText "string"; ageField, VInteger 13]
但不是这样:(在某种意义上,这将是一个编译时错误):



列表中的元组类型为
value*ty
。为了让编译器注意到您需要两个连接的状态,您需要让编译器“知道”您需要不同的、连接的状态。这可能需要您删除一些通用的属性:

type DataType = 
| TextData of VText * { Id : int; Type : TText; Name : string }
| IntData of VInteger * { Id : int; Type : TInteger; Name : string }
然后,您将创建一个
数据类型的列表
,编译器将注意到您是否尝试将一个VInteger混合到一个TText记录等中,因为您已经在一个有区别的并集中明确地声明了这些组合。
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 }

类型数据类型ValA
关系,如果您有许多可能的组合(此时您需要将DI重新设计为所有可能的树,或将变量数据重构为单独的记录),则在数值上会变得丑陋。

不可能完全按照您的要求执行。但是,这里有一些类似的方法也会起作用:

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"]

您将类型保存在一个记录中,数据保存在一个单独的记录中。它需要运行时检查数据是否有效。就编译器而言,您已经在一个列表中为它提供了两个独立的断开连接的类型。因为Convert/Use主体中的值类型是泛型的,所以除了在其上调用
ToString()
?@simonweijgers-您可以使用一个类型测试,类似于您在
值上匹配模式的方式。
type DataType<'a> = {
    Content :  'a * { Id : int; Type : 'a; Name : string }
type PossibleType = DataType<int> | DataType<string>
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 })