As others have been said, I think there is a misunderstanding here.
First, methods can (and often should) be annotated with their return types. And if a method is annotated as "T -> U" then it cannot return an error and trying to do so would fail the compilation - so I totally agree with you on that.
But inside of (non public) application or library code, there are a lot of small methods calling each other before there is some higher-up public method that will be called not by me but someone else.
And for those kind of methods type inference is great when it comes to refactoring. Even if I make a mistake and make such a method return an error even though it shouldn't, this mistake will be caught a bit higher up where the return type is annotated. In my experience this has almost no drawbacks and drastically simplifies refactorings.
I don't think there's a misunderstanding, I stand by what I said.
Inferred return types don't make refactoring easier. In my experience they make it much more difficult, because you have to look at function implementation to understand what it does. Particularly for error handling, you always want to see what errors a function may return even in library code.
And all that said, there's no distinction to me between internals and externally facing code when it comes to quality and standards. If you have a function, even if it is called once, it should always have its return type annotated. It is more correct than leaving it to be inferred by the compiler, because you are explicitly annotating the intended behavior of the abstraction. This makes it easier to write correct code and for verifying it by a reviewer.
> Particularly for error handling, you always want to see what errors a function may return even in library code.
Any half-decent tooling should do that, since GP was talking about compile-time type inference. Of course there are some situations where the type hints aren't available, but then we're back to a general discussion about type inference.
FWIW, I don't actually agree with the idea that set-theoretic unions solve error handling, but I'm fully on-board with opt-in type inference for errors.
> Inferred return types don't make refactoring easier. In my experience they make it much more difficult, because you have to look at function implementation to understand what it does.
My editor/IDE does show the inferred type to me though. I don't have to look at the function implementation. If I had to, I would agree with you.
I think locking use of a language into particular editors is a step several decades backward into Borland-land. I'm not at all confident that "just stop writing types in source code because the magic editor will show them anyway" is a better posture than "just have the magic editor auto-fill the types".
I totally get your point and it's valid. But I think this ship has sailed a long time ago.
With modern languages having adhoc polymorphism, inheritance and different kinds of dispatch as well as extension methods etc., without an IDE you are already lost anyways.
E.g. in rust you won't know where a trait is implemented and how it will behave, in kotlin you won't know if a method exists on the object or comes from somewhere else etc.
The trend is going to more supportive but also more complex languages and tooling is naturally adapting to that.
First, methods can (and often should) be annotated with their return types. And if a method is annotated as "T -> U" then it cannot return an error and trying to do so would fail the compilation - so I totally agree with you on that.
But inside of (non public) application or library code, there are a lot of small methods calling each other before there is some higher-up public method that will be called not by me but someone else.
And for those kind of methods type inference is great when it comes to refactoring. Even if I make a mistake and make such a method return an error even though it shouldn't, this mistake will be caught a bit higher up where the return type is annotated. In my experience this has almost no drawbacks and drastically simplifies refactorings.