I don’t think that’s a strong argument. It’s just function overloading that some languages enable, others don’t. Not a categorical difference.
But if you insist, it would look something like:
typedef ... cat;
typedef ... dog;
void cat_greet(cat *obj) {/*do stuff */}
void dog_greet(dog *obj) {/*do stuff */}
#define greet(X) _Generic((X), cat: cat_greet, dog: dog_greet)(X)
int main(...)
{
cat mycat;
dog mydog;
greet(&mycat);
greet(&mydog);
return 0;
} type
Cat = object # these are just 'struct' in a different skin
Dog = object
proc greet(c: Cat): string = "Meow"
proc greet(d: Dog): string = "Bark"
assert Dog().greet() == "Bark"
assert Cat().greet() == "Meow"