wtf = A of string | B of float
And then you'd return A("abc") or B(2.01) and your function would be () -> wtf
wtf = A of string | B of float
And then you'd return A("abc") or B(2.01) and your function would be () -> wtf
I think the point you're making is that Standard ML has certain restrictions in its type system that are necessary to make type inference possible: there are no union types, no subtypes (more recent work has found a way to make subtyping compatible with full H-M), polymorphism has to be introduced explicitly with `let`, and there are no higher-kinded types. And in some cases you'd argue those restrictions might incur more code overhead compared to a language that loosens those restrictions at the cost of less reliable type inference (e.g. Scala).
That's sort-of-but-not-really true: in my experience union types and subtypes are always bad ideas and there are better alternatives for any use case where you would use them. You never actually want "String | Int", you want a type with meaningful semantics (like `Result<String, Int>`).
https://www.cs.cornell.edu/courses/cs3110/2011sp/Lectures/le...
So in the original example, you can't unify "ssdf" and the int 33, but in the re-written version B("sfd") and A(33) have the same type and can be unified.
A good discussion of this can be found in https://dev.realworldocaml.org/variants.html#scrollNav-4