能否基于包含Name命名字段的F# record编写泛型函数消除重复代码?
报错原因
F# 默认的泛型类型推断会在函数首次使用时将泛型参数固定为具体类型,你写的shorter函数中直接使用record.Name,编译器会将'T绑定为第一次碰到的带Name属性的类型,后续传入其他类型就会触发类型不匹配报错。
可行解决方案
该需求完全可以实现,不需要局限于给每个记录类型添加成员函数的方式,以下是几种常用方案:
方案1:使用静态解析类型参数(SRTP,推荐)
这是对现有代码改动最小的方案,不需要修改原有记录类型定义,编译期完成类型检查,无运行时开销:
// 加inline关键字,使用静态解析的类型参数约束类型必须有string类型的Name属性 let inline shorter (record: ^T) = (getTheHandler (^T : (member Name : string) record)) ()
调用方式和你原先的写法完全一致:
shorter myRecordA // 正常编译运行 shorter myRecordB // 正常编译运行
只要传入的类型包含string类型的Name属性,都可以直接使用该函数。
方案2:公共接口实现
如果你更偏好常规OOP风格的写法,可以给所有带Name的记录类型统一实现公共接口:
// 定义公共接口 type IHasName = abstract member Name : string // 给原有记录类型添加接口实现 type RecordA = { Name: string // 其他原有字段 } with interface IHasName with member this.Name = this.Name type RecordB = { Name: string // 其他原有字段 } with interface IHasName with member this.Name = this.Name
之后shorter函数可以直接基于接口编写:
let shorter (record: IHasName) = (getTheHandler record.Name) ()
方案3:传入属性访问委托
完全不需要修改原有类型定义,也不需要用到SRTP语法,适合对F#高级特性不熟悉的场景:
// 将Name属性的访问逻辑作为参数传入 let shorter getName record = (getTheHandler (getName record)) ()
调用时只需传入对应的属性访问lambda即可:
shorter (fun r -> r.Name) myRecordA shorter (fun r -> r.Name) myRecordB
内容的提问来源于stack exchange,提问作者Flame
相关产品推荐
相关产品推荐

