Many languages do this (type inference), to some extent. The moment you have any expression that can evaluate to more than one type, though, it requires annotations.
pair x y = (x, y)
This function creates a pair for any two types, and doesn't require any annotations. >>> x = 1
>>> type(x)
<class 'int'>
>>> x = 'foo'
>>> type(x)
<class 'str'>Also, I'm not sure how a union type could be distinguished from an error during type inference. Maybe a nullable, but otherwise it'd be very tricky.