That sounds like a Wrapper<Func1Payload>, not a Ticket<Func1Call> that will become an extra parameter of Func2 whose only purpose is to prove to Func2 that you called Func1.
And you wanted to make sure that func2 and func3 can only be called after func1 has been called.
A wrapper on the output of func1 here would be awkward because then you return Wrapper<Func1Done>(bar0). But func2 does not even need a bar0 and neither does func3.
So the solution is to return (bar0, Wrapper<Func1Done>) from func1 where
I think this is a good argument for the Ticket, however, for the case where these three functions are generically useful, not just used in this specified order, I would write a specific function, just copy-paste of the bodies capturing the required ordering as an implementation detail.
Obviously if you are operating in a wide, concurrent async system then the Ticket and separate function calls is the better mechanism for the ordering.
That's fair but then you have to make your args a struct for this bespoke purpose; an anti pattern.
Also, many other functions can depend on the ticket from func_1. So making the ticket separate and generic on the process is the right (imo) solution here.
Why should this be such a bad anti pattern? Sure, a function might not need to work on the entire data model, but with pass by reference, does it matter that much? I dont see big negatives by using struct args, possibly wrapped in some typestate.
On the other hand, doesnt seprating args and typestate defeat the purpose? Since they can now be constructed separately.
It's because I use Ticket<T> in situations where I need to remember (force other users to use) a sequence of functions that take arguments not necessarily constructed by others.
//One could also place all the data in a giant struct and move that across all functions but that eventually leads to struct bloat unless we use an explicit state machine, in which case type state is better
Because you may want to share certain behaviors between the two wrapper types via generic impl
That sounds like a Wrapper<Func1Payload>, not a Ticket<Func1Call> that will become an extra parameter of Func2 whose only purpose is to prove to Func2 that you called Func1.
Maybe I misunderstood something.
Okay let's say you had three functions
func1(foo_0) -> bar0
func2(foo_1, foo_2) -> bar1
func3(foo_3, foo_3) -> bar2
And you wanted to make sure that func2 and func3 can only be called after func1 has been called.
A wrapper on the output of func1 here would be awkward because then you return Wrapper<Func1Done>(bar0). But func2 does not even need a bar0 and neither does func3.
So the solution is to return (bar0, Wrapper<Func1Done>) from func1 where
struct Wrapper<T>(//cheating ())
I think this is a good argument for the Ticket, however, for the case where these three functions are generically useful, not just used in this specified order, I would write a specific function, just copy-paste of the bodies capturing the required ordering as an implementation detail.
Obviously if you are operating in a wide, concurrent async system then the Ticket and separate function calls is the better mechanism for the ordering.
That's fair but then you have to make your args a struct for this bespoke purpose; an anti pattern.
Also, many other functions can depend on the ticket from func_1. So making the ticket separate and generic on the process is the right (imo) solution here.
Why should this be such a bad anti pattern? Sure, a function might not need to work on the entire data model, but with pass by reference, does it matter that much? I dont see big negatives by using struct args, possibly wrapped in some typestate.
On the other hand, doesnt seprating args and typestate defeat the purpose? Since they can now be constructed separately.
It's because I use Ticket<T> in situations where I need to remember (force other users to use) a sequence of functions that take arguments not necessarily constructed by others.
async fn write_buffer(buf: &mut [u8]) -> Ticket<BufferWritten>
//Best that it it's own function for readability
async fn complex_counter_logic(ctr: Arc<AtomicUsize>, ticket: Ticket<BufferWritten>) -> Ticket<ComplexCounterLogic>
//One could also place all the data in a giant struct and move that across all functions but that eventually leads to struct bloat unless we use an explicit state machine, in which case type state is better