By contrast to other answers, there are cases where returning different types is acceptable.
Example 1
sum(2, 3) → int
sum(2.1, 3.7) → float
In some statically typed languages, this involves overloads, so we can consider that there several methods, each one returning the predefined, fixed type. In dynamic languages, this may be the same function, implemented as:
var sum = function (a, b) {
return a + b;
};
Same function, different types of return value.
Example 2
Imagine you get a response from an OpenID/OAuth component. Some OpenID/OAuth providers may contain more information, such as the age of the person.
var user = authProvider.findCurrent();
// user is now:
// {
// provider: 'Facebook',
// name: {
// firstName: 'Hello',
// secondName: 'World',
// },
// email: 'hello.world@example.com',
// age: 27
// }
Others would have the minimum, would it be an email address or the pseudonym.
var user = authProvider.findCurrent();
// user is now:
// {
// provider: 'Google',
// email: 'hello.world@example.com'
// }
Again, same function, different results.
Here, the benefit of returning different types is especially important in a context where you don't care about types and interfaces, but what objects actually contain. For example, let's imagine a website contains mature language. Then the findCurrent()
may be used like this:
var user = authProvider.findCurrent();
if (user.age || 0 >= 16) {
// The person can stand mature language.
allowShowingContent();
} else if (user.age) {
// OpenID/OAuth gave the age, but the person appears too young to see the content.
showParentalAdvisoryRequestedMessage();
} else {
// OpenID/OAuth won't tell the age of the person. Ask the user himself.
askForAge();
}
Refactoring this into code where every provider will have its own function which will return a well-defined, fixed type would not only degrade the code base and cause code duplication, but also will not bring any benefit. One may end up doing horrors like:
var age;
if (['Facebook', 'Yahoo', 'Blogger', 'LiveJournal'].contains(user.provider)) {
age = user.age;
}
Statically typed language: a language that does not allow you to change the type of a variable at run-time.
That's not the definition of statically typed. Statically typed means that type checking (and type inference) happens before runtime. The opposite is dynamic typing, where type checking happens at runtime.
It is perfectly possible to design a statically typed language in which either identifiers can change types or types can change.
Type safety: type safety means that you are not allowed to mix incompatible data types.
For example, you cannot assign a float
to an int
, and you cannot assign an int
to a function pointer, and you cannot add a user-defined object to an int
(unless you use operator overloading), etc.
There is no universally accepted definition of type safety. Yours is a very sensible one.
Back to my question, let's say that there is an interpreted statically typed language,
There is no such thing as an interpreted statically typed language, simply because there is no such thing as an interpreted language, period. Interpretation and compilation are traits of the, well, interpreter or compiler (duh!), i.e. the implementation, not the language. Every language can be implemented with an interpreter and every language can be implemented with a compiler. In fact, many languages have both interpreted and compiled implementations. For example, Haskell has several compiled implementations (Ghc, Jhc, Yhc, Lhv) and an interpreted implementation (Hugs). ECMAScript has pure compiled implementations (V8), and hybrid mixed-mode implementations (e.g. SpiderMonkey AOT-compiles ECMAScript to SpiderMonkey bytecode, then both compiles and interprets this bytecode)
Saying that a language is an "interpreted language" is not just wrong, it is even more than wrong, it is simply non-sensical. The idea of "language" and the idea of "interpretation" live on two different levels of abstraction. If English were a typed language, "interpreted language" would be a type error.
in such a language I can write code that assigns a float
to an int
, but when this code is executed, a (run-time) type error will occur.
You are inconsistent with yourself here.
You say "statically typed" means "does not allow to change the type of a variable", which means you can not write such code. But now you say that you can write such code in a statically typed language.
But if you can write such code, then by your own definition, it is not statically typed. And if it is statically typed, then by your own definition, you cannot write such code.
So, it's not surprising that you get a contradiction, because you are contradicting yourself.
Is such a language considered to be type safe, or are type safe languages can only be statically typed and compiled, and so type errors must be caught at the compilation stage?
Whether or not type safe languages can only be statically typed or not really only depends on how you define "type safe". Another common definition of "type safe" is that you cannot subvert the type system. Ruby is dynamically typed, but it is usually described as type safe. On the other hand, C is statically typed but nobody would describe it as type safe.
As for whether a type safe language can only be compiled, I already explained above that compilation is an implementation detail, it is not a property of the language itself.
Best Answer
It's not trivial. It's not trivial at all. It's only trivial to do this for trivial functions.
For instance, you could trivially define a function where the return type depends entirely on the input type.
In this case,
getAnswer
doesn't really have a single return type. There is no test you can ever write that calls this with a sample input to learn what the return type is. It will always depend on the actual argument. At runtime.And this doesn't even include functions that, e.g., perform database lookups. Or do things based on user input. Or look up global variables, which are of course of a dynamic type. Or change their return type in random cases. Not to mention the need to test every single individual function manually every single time.
Fundamentally, proving the return type of the function in the general case is literally mathematically impossible (Halting Problem). The only way to guarantee the return type is to restrict the input so that answering this question does not fall under the domain of the Halting Problem by disallowing programs that are not provable, and this is what static typing does.
Statically typed languages have things called "tools". They are programs that help you do things with your source code. In this case, I would simply right click and Go To Definition, thanks to Resharper. Or use the keyboard shortcut. Or just mouse over and it will tell me what the types involved are. I don't care in the slightest about grepping files. A text editor on its own is a pathetic tool for editing program source code.
From memory,
def funcname
would not be enough in Python, as the function could be re-assigned arbitrarily. Or could be declared repeatedly in multiple modules. Or in classes. Etc.Searching files for the function name is a terrible primitive operation that should never be required. This represents a fundamental failure of your environment and tooling. The fact that you would even consider needing a text search in Python is a massive point against Python.