Remix.run Logo
vatsachak a day ago

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.

throwawayqqq11 a day ago | parent | next [-]

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.

vatsachak a day ago | parent | next [-]

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

binary132 14 hours ago | parent [-]

Why not return a WrittenBuffer<'a>? This can also be used to specify the methodset allowed or required plus any further type transitions out of WrittenBuffer, for example into a new CompressedBuffer, or similar patterns.

vatsachak 11 hours ago | parent [-]

That's fair, but you would have to make sure that write_buffer is the only function that can create a WrittenBuffer<'a>. And then the second function could take a WrittenBuffer<'a> as an unused arg

I mean there's many ways to skin a cat!

binary132 14 hours ago | parent | prev [-]

I think my point is that types and “typestate” do not need to be two separate things. For example, in the Lua API for C, one obviously requires a Lua context handle in order to perform any other operations, so the handle must be obtained first, by calling the context initializer function. There is no need for an extra “typestate” parameter since the dependency is explicit and enforced.

vatsachak 11 hours ago | parent [-]

My Ticket example is not the standard type state pattern. Usually the State is shimmied in as a generic into the type being mutated

binary132 14 hours ago | parent | prev [-]

There is no cost imposed by wrapping a type in a struct to enforce a dependency.