如何从多个记录类型访问相同的标签?

时间:2013-08-07 02:40:42

标签: f# label record

是否可以让一个函数从不同的记录类型中调用相同的标签?例如,假设有两条记录,定义如下

type Pen = {
    Diameter: float
    InkColor: string
}

type Pencil = {
    Diameter: float
    Hardness: int
    Blackness: int
}

我可以创建一个函数来从任一记录类型访问Diameter标签吗?现在,如果我定义一支钢笔和铅笔,编译器会对使用哪种记录类型感到困惑。问题是我不希望编译器选择一种类型,如果确实选择了某种类型,则允许使用这两种类型。这个例子不会编译,因为它需要一支铅笔。

let black_pen = {
    Diameter = 0.7
    InkColor = "Black"
}

let mechanical_pencil = {
    Diameter = 0.5
    Hardness = 1
    Blackness = 2
}

let getDiameter writing_utility = 
    let {Diameter = dia} = writing_utility
    dia

printf "%A" (getDiameter black_pen)

我现在看到的唯一选择是:

  1. 将记录与枚举类型组合以告知哪个是对象。然后模式匹配
  2. 使用类来代替使用inherit
  3. 使用动态类型和反射检查标签并输入
  4. 如果我可以使用泛型来做这样的事情会很好:

    let getDiameter writing_utility = 
        let {Diameter<float> = dia} = writing_utility
        dia
    

    只要记录有一个标签“Diameter”并且是一个浮点数,它就会返回该值。

3 个答案:

答案 0 :(得分:5)

你真的应该使用继承,但以下工作

let inline getDiameter (t:^q when ^q :( member Diameter:float)) = 
    (^q : (member Diameter:float) t);; 

答案 1 :(得分:2)

以下几个选项。

提供从“具有直径的东西”到“直径”的映射作为函数:

let getDiameter (util:'a) (diamFunc:'a->float) = 
    let dia = diamFunc util
    dia

getDiameter black_pen (fun x -> x.Diameter)
getDiameter mechanical_pencil (fun x -> x.Diameter)

或者更干净,使用DU(很好地使用F#3.1命名为DU字段语法):

type WritingImplement =
    | Pen of diameter:float * inkColor:string
    | Pencil of float * int * int  // 3.0 syntax

let black_pen = Pen(diameter = 0.7, inkColor = "Black")
let mechanical_pencil = Pencil(0.5, 1, 2)  // 3.0 syntax

let getDiameter = function
    | Pen(diameter = d) -> d
    | Pencil(d, _, _) -> d  // 3.0 syntax

答案 2 :(得分:2)

考虑getDiameter做什么。它将something映射到float,即'a -> float,但它没有意义,因为'a表示它可以是任何内容,并将任何内容映射到float值了解它的一些事情是行不通的。我们需要确保我们知道有关传递的东西的一些属性来获取float值,即我们想要类似<something with Diameter> -> float的东西,并且表示对某些东西的约束的最佳方式是使用接口,使得签名现在变为IWithDiameter -> float