F#

时间:2016-09-13 15:49:25

标签: f#

我有两个示例类型,按此顺序定义:

type Quote = {QuoteNum: decimal option; ShipToID: decimal option}
type Sales = {SalesNum: decimal option; ShipToID: decimal option}

我正在尝试编写一个可以接受这两种类型的函数:

let fx (y: Map<decimal option, _ list>) = 
    y
    |> Map.map (fun key list -> list
                                   |> List.map (fun x -> x.ShipToID))

当我尝试将Map<decimal option, Quote list>传递给函数时,出现错误:

Type mismatch. Expecting a
Map<decimal option,Sales list>    
but given a
    Map<decimal option,Quote list>

我原本以为我能够将两种类型的映射传递给函数,但编译器似乎推断该函数只能接受Map<decimal option, Sales list>。我怀疑编译器最近“看到”Sales类型,并假设这是函数所需要的。我以为我通过在类型注释中包含_来使函数相对通用。

如何让函数接受这两种类型?如果不重新定义类型,我可以这样做吗?

2 个答案:

答案 0 :(得分:4)

你的怀疑是正确的。 F#需要在那里推断出单个具体类型,因此将使用它找到的最新匹配类型。

您可以通过定义具有ShipToID字段的接口并使两种类型实现它(不确定是否计算为您重新定义类型),或者通过提供用于提取字段值的getter函数来使其工作(这会让你保持功能通用):

let fx (shipToIdGetter: 'a -> decimal option) (y: Map<decimal option, 'a list>) = 
    y
    |> Map.map (fun key list -> 
                   list
                   |> List.map (fun x -> shipToIdGetter x))

如果F#支持行多态(如果OCaml就是这种情况),您的代码将按照写入的方式工作。

答案 1 :(得分:4)

虽然我认为scrwtp's答案是要走的路,但你也可以选择使用内联和member constraints(或静态鸭子打字......如果这是一个有效的术语)。为了完整起见:

type Quote = {QuoteNum: decimal option; ShipToID: decimal option}
type Sales = {SalesNum: decimal option; ShipToID: decimal option}

let inline fx (y: Map<decimal option, 'a list>) = 
    let inline getId x = ( ^a : (member ShipToID : decimal option) x)

    y |> Map.map (fun key list -> list |> List.map getId)


let q : Quote = { QuoteNum = None; ShipToID = None}
let s : Sales = { SalesNum = None; ShipToID = None}

fx <| Map.ofList [ None, [q]]
fx <| Map.ofList [ None, [s]]

getId函数使用相当模糊的语法根据ShipToID的预期结构调用^a成员。