Some feedback, but not necessarily a call to make changes.
At the end of the part 2 video on Polymorphism, there is mention that using Unions are “not extensible” and a rawptr solves this. I don’t disagree with those statements exactly, and I’m certain I’m not seasoned enough an Odin user to have an opinion on this, but my reaction to that part is as follows:
I tend to think of the extensibility of code and the method chosen to be based on use-case. Take for example a library intended for 3rd party use. I would not provide rawptr as the main entry point into the library and it’s procedures. There’s no way the library writer can be certain that whatever is assigned to the rawptr contains the expected struct definitions. So, personally, I would reserve this approach to creating interfaces for code that is either for personal use and not shared as a library, or for code projects where code reviews are performed to ensure any new struct entities adhere to the expected structure in the rawptr.
For 3rd party “extensibility” I might suggest unions instead. There’s a stronger level of control and certainty. Combining different Odin approaches can yield some reliability while still maintaining a supportable 3rd party library with flexibility within boundaries.
So for example, I might combine unions with procedure overloading and intrinsics specialization do the following with an overly simplified example:
Cat :: struct {
name: string,
age: int,
}
Dog :: struct {
name: string,
age: int,
}
Pet :: union {
Cat,
Dog,
}
print_pet :: proc {print_pet_struct, print_pet_union}
print_pet_struct :: proc(p: $P) where intrinsics.type_is_variant_of(Pet, P) {
fmt.printfln("%v %v", p.name, p.age)
}
print_pet_union :: proc(p: Pet) {
switch v in p {
case Cat: print_pet_struct(v) // or could do print_pet(v) and the correct procedure will be selected
case Dog: print_pet_struct(v) // or could do print_pet(v) and the correct procedure will be selected
}
}
main :: proc() {
pet: Pet
cat := Cat{"Meow Face", 6}
dog := Dog{"Woof Woof", 3}
// print using explicit struct definitions
print_pet(cat)
print_pet(dog)
// print using dynamic struct definitions
pet = cat
print_pet(pet)
pet = dog
print_pet(pet)
}
The above feels as though I’ve provided options on how to print the pet information without requiring the use of the union type Pet, but yet still have boundaries on what’s possible. It does require a little more work on the back end, but front end 3rd party users can still have reasonable flexibility.
Anyone more seasoned feel different?
Upon further thought, I may be splitting hairs on the closed-set of variants with an open-set of functionality vs. open-set of variants with a closed-set of functionality idea.