Reputation: 9934
This is what I'd like to do:
type DirectionBuilder() =
member self.Yield(()) = []
[<CustomOperation("left")>]
member self.Left (acc, degree) = None
[<CustomOperation("right")>]
member self.Right (acc, degree) = None
[<CustomOperation("velocity")>]
member self.Velocity (acc, ()) = new VelocityBuilder()
and VelocityBuilder() =
member self.Yield(()) = []
[<CustomOperation("accelerate")>]
member self.Accesslarate (acc, v) = None
[<CustomOperation("decelerate")>]
member self.Decelerate (acc, v) = None
let direction () = new DirectionBuilder()
direction() {
right 90
left 30
velocity() {
accelerate 1
}
}
This breaks down at line
velocity() {
----^^^^^^^^
stdin(952,5): error FS3099: 'velocity' is used with an incorrect number of
arguments. This is a custom operation in this query or computation
expression. Expected 1 argument(s), but given 2.
>
Is there anyway to explain to F# that this custom operation should indeed accept a computation expression?
Upvotes: 4
Views: 866
Reputation: 13577
I suppose there is a way to almost get the syntax you want. You make velocity
operation accept a type that the VelocityBuilder produces - which in your case appears to be an option
. Then you create a separate computation and pass it in.
So you get something like this:
type DirectionBuilder() =
member self.Yield(()) = []
[<CustomOperation("left")>]
member self.Left (acc, degree) = None
[<CustomOperation("right")>]
member self.Right (acc, degree) = None
[<CustomOperation("velocity")>]
member self.Velocity (acc, odd: 'a option) = None
type VelocityBuilder() =
member self.Yield(()) = []
[<CustomOperation("accelerate")>]
member self.Accelerate (acc, v) = None
[<CustomOperation("decelerate")>]
member self.Decelerate (acc, v) = None
let dir = new DirectionBuilder()
let vel = new VelocityBuilder()
dir {
right 90
left 30
velocity (vel {
accelerate 1
})
}
That said, if you set out to write computation workflows, you probably should start by designing a type to represent the state of your computation. Right now you have syntax sugar, but no meat ;)
Once you have a type, a workflow can follow from that if it turns out to be useful.
Upvotes: 2