where I need to make sure certain functions are called in order
The ticket approach is a neat way to handle this, but I’ve always felt that functions needing to be called in a specific order is usually a bad code smell.
I’m sure there are times it’s unavoidable or maybe even the cleanest approach, but I don’t think I’ve encountered one in my career. When are you finding you need to do this?
All I’m saying is that having a set of functions that must be called in a specific order is often a code smell. Forcing them to be called in the right order improves the ergonomics, but doesn’t eliminate the smell.
If there is no shared information in the ticket other than the fact that the earlier method was called, then you’re almost certainly modifying global hidden state. If you can avoid that, all the better.
If you do need to package data along with the ticket, you can just use regular structs. Which is usually better from a naming perspective anyway.
let gpu : GPU = GPU::initialize(…);
let ctx : Context = gpu.create_context(…);
The ticket pattern is just plain old structs but with a (usually unnecessary) layer of generics.
let t1 : Ticket<GPU> = GPU::initialize();
let t2 : Ticket<Context> = GPU::create_context(t1);
Personally, I think the main legitimate use of the ticket pattern is when dealing with hardware. In which case your global hidden state is actually the physical world and can't be eliminated.
Comments
The ticket approach is a neat way to handle this, but I’ve always felt that functions needing to be called in a specific order is usually a bad code smell.
I’m sure there are times it’s unavoidable or maybe even the cleanest approach, but I don’t think I’ve encountered one in my career. When are you finding you need to do this?
Can you give an example where a different design eliminates the need for the ticket pattern?
This entirely depends on the underlying problem.
All I’m saying is that having a set of functions that must be called in a specific order is often a code smell. Forcing them to be called in the right order improves the ergonomics, but doesn’t eliminate the smell.
If there is no shared information in the ticket other than the fact that the earlier method was called, then you’re almost certainly modifying global hidden state. If you can avoid that, all the better.
If you do need to package data along with the ticket, you can just use regular structs. Which is usually better from a naming perspective anyway.
The ticket pattern is just plain old structs but with a (usually unnecessary) layer of generics.Thanks, that was insightful.
Personally, I think the main legitimate use of the ticket pattern is when dealing with hardware. In which case your global hidden state is actually the physical world and can't be eliminated.
Otherwise, I think you have a good point.
Making sure something can be only used correctly is bad? Why?
Making functions that must be called in a specific, pre-defined order is the problem.
Forcing it to be done correctly through the type system is a neat trick, but better is to design it so the trick wasn’t needed in the first place.