我有两个示例类型,按此顺序定义:
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
类型,并假设这是函数所需要的。我以为我通过在类型注释中包含_
来使函数相对通用。
如何让函数接受这两种类型?如果不重新定义类型,我可以这样做吗?
答案 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
成员。