F# 实现依赖于部分记录类型的惯用方式是什么?
我的目的是定义一个模块,该模块具有可以对所有记录类型进行操作的函数,这些记录类型符合有关键的某些假设 为了进行说明,让我们使用以下代码:F# 实现依赖于部分记录类型的惯用方式是什么?,f#,F#,我的目的是定义一个模块,该模块具有可以对所有记录类型进行操作的函数,这些记录类型符合有关键的某些假设 为了进行说明,让我们使用以下代码: > type DBRow = { id: string ; createdAt: System.DateTime } ;; type DBRow = {id: string; createdAt: System.DateTime;} > let logCreationInfo row = printf "Record %s create
> type DBRow = { id: string ; createdAt: System.DateTime } ;;
type DBRow =
{id: string;
createdAt: System.DateTime;}
> let logCreationInfo row = printf "Record %s created at %s " row.id (row.createdAt.ToString()) ;;
val logCreationInfo : row:DBRow -> unit
我想更改上面的logCreationInfo
,以便能够对具有id:string
和createdAt:System.DateTime
的所有记录进行操作(可能还有其他内容)
从typescript的结构类型来看,我本以为这很简单,但我正在探索一种可能性,即有一种更惯用的方法在F#中处理这个问题
我必须使用接口来处理这个问题,但即使这样做可行,因为F#只支持显式接口,这将不适用于我自己没有定义的类型 你可以用
下面是一个带有接口的版本:
open System
type DBRow1 = {
id: string
createdAt: DateTime
}
type DBRow2 = {
id: string
createdAt: DateTime
address: string
}
/// The types are defined above without an interface
let row1 = {id = "Row1"; createdAt = DateTime.Now}
let row2 = {id = "Row2"; createdAt = DateTime.Now; address = "NYC"}
type IDBRow<'A> =
abstract member Data:(string * DateTime)
// Object expression implements the interface
let Data1 (x:DBRow1) = {
new IDBRow<_> with
member __.Data = (x.id, x.createdAt)
}
let Data2 (x: DBRow2) = {
new IDBRow<_> with
member __.Data = (x.id, x.createdAt)
}
//pass in both the object expression and the record
let getData (ifun: 'a -> IDBRow<'b>) xrec =
(ifun xrec).Data
// You could partially apply the functions: `getData1 = getData Data1`
getData Data1 row1 //("Row1", 2018/02/05 9:24:17)
getData Data2 row2 //("Row2", 2018/02/05 9:24:17)
开放系统
类型DBRow1={
id:字符串
createdAt:DateTime
}
类型DBRow2={
id:字符串
createdAt:DateTime
地址:string
}
///上面定义的类型没有接口
让row1={id=“row1”;createdAt=DateTime.Now}
让row2={id=“row2”;createdAt=DateTime.Now;address=“NYC”}
类型IDBRow IDBRowF#主要使用命名类型-这是其运行时环境中的自然选择,因为这是规范规定的。遵守这套规则可以让F#代码与其他.NET语言进行近乎无缝的互操作
值得注意的是,这与为什么TypeScript使用结构类型相同。由于该语言建立在动态类型JavaScript之上,因此更自然的做法是根据对象的结构而不是名义类型来表达对象关系,这在JS中是一个陌生的概念
F#确实有一个“后门”用于通过已经提到的SRTPs进行结构键入,但我建议非常谨慎地使用它。SRTP被解析,使用它们的代码由编译器内联,从而延长了编译时间,降低了与其他语言和.NET平台的互操作性(简单地说,您不能从其他语言引用该代码或使用反射API,因为它是“编译掉的”)
通常还有其他可用的解决方案。已经提到了接口,尽管使用的示例有点做作-这更简单:
type IDBRow =
abstract Id: string
abstract CreatedAt: System.DateTime
type Person =
{
id: string
name: string
age: int
createdAt: System.DateTime
}
interface IDBRow with
member this.Id = this.id
member this.CreatedAt = this.createdAt
let logCreationInfo (row: #IDBRow) =
printf "Record %s created at %s" row.Id (string row.CreatedAt)
let x = { id = "1"; name = "Bob"; age = 32; createdAt = DateTime.Now }
logCreationInfo x
或者使用组合和泛型类型来捕获DBRow的泛型部分:
type DBRow<'data> =
{
id: string
data: 'data
createdAt: System.DateTime
}
type Person =
{
name: string
age: int
}
let logCreationInfo (row: DBRow<_>) =
printf "Record %s created at %s" row.id (string row.createdAt)
let x = { id = "1"; data = { name = "Bob"; age = 32 }; createdAt = DateTime.Now }
logCreationInfo x
类型DBRowSRTP可能是访问不同类型成员的最简单方法。但是,您也可以使用接口执行上述操作。但是tbh,一种更简单的方法将非常受欢迎(比如Scala中的类型类和隐式类)。
type DBRow<'data> =
{
id: string
data: 'data
createdAt: System.DateTime
}
type Person =
{
name: string
age: int
}
let logCreationInfo (row: DBRow<_>) =
printf "Record %s created at %s" row.id (string row.createdAt)
let x = { id = "1"; data = { name = "Bob"; age = 32 }; createdAt = DateTime.Now }
logCreationInfo x