构造复杂对象图的计算表达式
Computation Expression for constructing complex object graph
给定以下类型:
type Trip = {
From: string
To: string
}
type Passenger = {
Name: string
LastName: string
Trips: Trip list
}
我正在使用以下构建器:
type PassengerBuilder() =
member this.Yield(_) = Passenger.Empty
[<CustomOperation("lastName")>]
member __.LastName(r: Passenger, lastName: string) =
{ r with LastName = lastName }
[<CustomOperation("name")>]
member __.Name(r: Passenger, name: string) =
{ r with Name = name }
type TripBuilder() =
member __.Yield(_) = Trip.Empty
[<CustomOperation("from")>]
member __.From(t: Trip, f: string) =
{ t with From = f }
// ... and so on
创建 Passenger
类型的记录,如下所示:
let passenger = PassengerBuilder()
let trip = TripBuilder()
let p = passenger {
name "john"
lastName "doe"
}
let t = trip {
from "Buenos Aires"
to "Madrid"
}
我该如何结合 PassengerBuilder
和 TripBuilder
才能实现这种用法?
let p = passenger {
name "John"
lastName "Doe"
trip from "Buenos Aires" to "Madrid"
trip from "Madrid" to "Paris"
}
那 returns 一条 Passenger
记录如:
{
LastName = "Doe"
Name = "John"
Trips = [
{ From = "Buenos Aires"; To = "Madrid" }
{ From = "Madrid"; To = "Paris" }
]
}
我不确定这是否是您想要的,但是没有什么能阻止您在 PassengerBuilder
:
上创建一个名为 trip
的新操作
[<CustomOperation("trip")>]
member __.Trip(r: Passenger, t: Trip) =
{ r with Trips = t :: r.Trips }
然后像这样使用它:
let p = passenger {
name "John"
lastName "Doe"
trip (trip { from "Buenos Aires"; to "Madrid" })
trip (trip { from "Madrid"; to "Paris" })
}
可以说,您甚至可以通过完全删除 TripBuilder
来使其更干净:
let p = passenger {
name "John"
lastName "Doe"
trip { From = "Buenos Aires"; To = "Madrid" }
trip { From = "Madrid"; To = "Paris" }
}
如果这不是您想要的,请具体说明。也就是说,这个解决方案缺少什么或多了什么。
您想使用计算表达式生成器有什么原因吗?根据您的示例,您似乎没有写任何东西 computation-like。如果你只是想要一个很好的 DSL 来创建旅行,那么你可以很容易地定义一些让你写的东西:
let p =
passenger [
name "John"
lastName "Doe"
trip from "Buenos Aires" towards "Madrid"
trip from "Madrid" towards "Paris"
]
这几乎正是您所要求的,除了它使用 [ .. ]
而不是 { .. }
(因为它创建了一个转换列表)。我还将 to
重命名为 towards
因为 to
是一个关键字,您不能重新定义它。
此代码很容易编写和遵循:
let passenger ops =
ops |> List.fold (fun ps op -> op ps)
{ Name = ""; LastName = ""; Trips = [] }
let trip op1 arg1 op2 arg2 ps =
let trip =
[op1 arg1; op2 arg2] |> List.fold (fun tr op -> op tr)
{ From = ""; To = "" }
{ ps with Trips = trip :: ps.Trips }
let name n ps = { ps with Name = n }
let lastName n ps = { ps with LastName = n }
let from n tp = { tp with From = n }
let towards n tp = { tp with To = n }
也就是说,我仍然会考虑使用普通的 F# 记录语法 - 它并没有比这更难看。上述版本的一个缺点是您可以创建具有空名称和姓氏的乘客,这是 F# 阻止您做的一件事!
给定以下类型:
type Trip = {
From: string
To: string
}
type Passenger = {
Name: string
LastName: string
Trips: Trip list
}
我正在使用以下构建器:
type PassengerBuilder() =
member this.Yield(_) = Passenger.Empty
[<CustomOperation("lastName")>]
member __.LastName(r: Passenger, lastName: string) =
{ r with LastName = lastName }
[<CustomOperation("name")>]
member __.Name(r: Passenger, name: string) =
{ r with Name = name }
type TripBuilder() =
member __.Yield(_) = Trip.Empty
[<CustomOperation("from")>]
member __.From(t: Trip, f: string) =
{ t with From = f }
// ... and so on
创建 Passenger
类型的记录,如下所示:
let passenger = PassengerBuilder()
let trip = TripBuilder()
let p = passenger {
name "john"
lastName "doe"
}
let t = trip {
from "Buenos Aires"
to "Madrid"
}
我该如何结合 PassengerBuilder
和 TripBuilder
才能实现这种用法?
let p = passenger {
name "John"
lastName "Doe"
trip from "Buenos Aires" to "Madrid"
trip from "Madrid" to "Paris"
}
那 returns 一条 Passenger
记录如:
{
LastName = "Doe"
Name = "John"
Trips = [
{ From = "Buenos Aires"; To = "Madrid" }
{ From = "Madrid"; To = "Paris" }
]
}
我不确定这是否是您想要的,但是没有什么能阻止您在 PassengerBuilder
:
trip
的新操作
[<CustomOperation("trip")>]
member __.Trip(r: Passenger, t: Trip) =
{ r with Trips = t :: r.Trips }
然后像这样使用它:
let p = passenger {
name "John"
lastName "Doe"
trip (trip { from "Buenos Aires"; to "Madrid" })
trip (trip { from "Madrid"; to "Paris" })
}
可以说,您甚至可以通过完全删除 TripBuilder
来使其更干净:
let p = passenger {
name "John"
lastName "Doe"
trip { From = "Buenos Aires"; To = "Madrid" }
trip { From = "Madrid"; To = "Paris" }
}
如果这不是您想要的,请具体说明。也就是说,这个解决方案缺少什么或多了什么。
您想使用计算表达式生成器有什么原因吗?根据您的示例,您似乎没有写任何东西 computation-like。如果你只是想要一个很好的 DSL 来创建旅行,那么你可以很容易地定义一些让你写的东西:
let p =
passenger [
name "John"
lastName "Doe"
trip from "Buenos Aires" towards "Madrid"
trip from "Madrid" towards "Paris"
]
这几乎正是您所要求的,除了它使用 [ .. ]
而不是 { .. }
(因为它创建了一个转换列表)。我还将 to
重命名为 towards
因为 to
是一个关键字,您不能重新定义它。
此代码很容易编写和遵循:
let passenger ops =
ops |> List.fold (fun ps op -> op ps)
{ Name = ""; LastName = ""; Trips = [] }
let trip op1 arg1 op2 arg2 ps =
let trip =
[op1 arg1; op2 arg2] |> List.fold (fun tr op -> op tr)
{ From = ""; To = "" }
{ ps with Trips = trip :: ps.Trips }
let name n ps = { ps with Name = n }
let lastName n ps = { ps with LastName = n }
let from n tp = { tp with From = n }
let towards n tp = { tp with To = n }
也就是说,我仍然会考虑使用普通的 F# 记录语法 - 它并没有比这更难看。上述版本的一个缺点是您可以创建具有空名称和姓氏的乘客,这是 F# 阻止您做的一件事!