【问题标题】:F# member constraints on return type返回类型的 F# 成员约束
【发布时间】:2017-12-18 07:36:12
【问题描述】:

假设我有以下类型:

type AddressLow = {
    FlatNo: int
    PinCode: string
}

type AddressHigh = {
    FlatNo: int
    AreaName: string
    PinCode: string
}

type PersonDataLow = {            
    id:int
    name:string
    address: AddressLow
}

type PersonDataHigh = { //same label names, different type for address
    id:int
    name:string
    address: AddressHigh 
}

以下两个函数是构建地址:

let GetAddressLow () =
    {AddressLow.FlatNo = 10; PinCode = "5245"}

let GetAddressHigh () =
    {AddressHigh.FlatNo = 10; AreaName = "Bel Air"; PinCode = "8225"}

以下功能是构建 PersonData:

let GetPerson fGetAddress inputId inputName = //return type inferred as PersonDataHigh
    {
        id = inputId
        name = inputName
        address = fGetAddress()
    }

let p1 = GetPerson GetAddressLow 4 "John Smith" //compile error
let p2 = GetPerson GetAddressHigh 6 "Will Smith" //works

对于上述函数,返回类型被 F# 推断为PersonDataHigh。 因此,要返回不同类型的 PersonData(即 PersonDataHighPersonDataLow),我必须编写两个不同的函数。

另一种方法是使用可区分联合 (DU),但这涉及 DU 类型和 DU 类型的 case-identifiers 之间的来回转换次数。

是否可以对返回类型使用约束以便只编写一次函数?说,像这样:

let inline GetPerson (fGetAddress) (inputId) (inputName) 
    : ^T when ^T: (member id: int) and ^T: (member name: string) and (^T: (member address: AddressLow) or ^T: (member address: AddressHigh)) = //compile error
    {
        id = inputId
        name = inputName
        address = fGetAddress()
    }

如果不是,那么在这里使用 DU 是不是最好的选择?我正在使用 F# 3.0。

谢谢。

【问题讨论】:

  • 你可以使用泛型:type PersonData<'T> = { id : int; name : string; address: 'T }
  • 当然是泛型。谢谢李。只是出于好奇,是否可以在 F# 中使用“and”和“or”条件有多个成员约束?

标签: types f# constraints


【解决方案1】:

您是否考虑过在单个 Address 类型中嵌套低或高?

由于您的大部分数据在Address 的两种类型之间共享,因此我认为将其设为有区别的联合或两种不同类型并不是最明智的选择。相反,只需将其属性之一设为有区别的联合即可。

最简单的方法是将AreaName 设为option

type Address = {
    FlatNo: int
    AreaName : string option
    PinCode: string
}

type PersonData = {            
    id:int
    name:string
    address: Address
}

那你就可以走了:

let GetAddressLow () =
    {FlatNo = 10; AreaName = None; PinCode = "5245"}

let GetAddressHigh () =
    {FlatNo = 10; AreaName = Some "Bel Air"; PinCode = "8225"}

然后你就不需要任何花哨的东西来创建你的GetPerson 函数了。

【讨论】:

  • 在我们的例子中,我们必须为 High 和 Low 使用不同的类型。我认为仿制药可能是李所建议的最简单的解决方案。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-07
  • 1970-01-01
  • 2020-12-20
  • 2016-11-13
  • 2012-12-29
相关资源
最近更新 更多