9

c, java and many other languages do not pay attention to return values.

int   i = func()
float f = func()
int   func() { return 5 }
float func() { return 1.3}

Why isnt the above legal? Does it make it more difficult to program

int i = func(func(func(func2(func3())))) //you dont know what you are getting

Is it hard to write a compiler? are there more language unambiguity? Is there a language that can do the above?

Jacob
  • 34,255
  • 14
  • 110
  • 165
  • Have you tried LISP?? If not try it, it might solve your problem. – Zinx Oct 20 '09 at 19:11
  • possible duplicate of [Function overloading by return type?](http://stackoverflow.com/questions/442026/function-overloading-by-return-type) – nawfal Jun 05 '13 at 13:22

8 Answers8

17

What about this case?

class A implements Foo { /*...*/ }
class B implements Foo { /*...*/ }

A func() { return new A(); }
B func() { return new B(); }

Foo v = func(); // which do you call?!

There are already problems with ambiguity when you allow overloading of a single function name that take different arguments. Having to check the return type as well would probably make resolving the right function a lot harder.

I'm sure a language could implement that, but it would make things a lot more complicated, and would generally make the code harder to understand.

Herms
  • 37,540
  • 12
  • 78
  • 101
  • 2
    Similarly what should `System.out.println(func() + func())` output? – notnoop Oct 20 '09 at 19:01
  • That would make code harder to understand. +1. Maybe i'll mark this as correct (i want to see other answers) –  Oct 20 '09 at 19:18
  • How about requiring one of the overloads to be marked as "preferred", with others being used only when the return type would be immediately cast or coerced to another type (for which a more precise overload is provided), or when the return type is ignored (in which case a `void` overload would be used if supplied)? – supercat Oct 09 '12 at 23:29
14

Let's say it was allowed, which one would this call:

func();

That may not the the full answer, but I believe that is one reason why it is not legal.

Fredrik Mörk
  • 155,851
  • 29
  • 291
  • 343
  • 1
    that's a good point. Parameters are generally required for functions, but handling the return value isn't. – Herms Oct 20 '09 at 19:01
  • 2
    I think it is bad practice to not handle return values. This should be void throwning an exception or the return value should be checked. However var v = func(); (C# style assignment) may cause problems... (hmmmm)) –  Oct 20 '09 at 19:16
  • I'm not sure I agree with you on the "bad practice" part, at least not in the general case. It's certainly not something that should be explicitly forbidden, so the language would have to be able to handle this. – Herms Oct 20 '09 at 19:21
  • A good point, but rules could also be added to set a default implementation of Func(), either explicitly or by some set of rules. – captncraig Nov 04 '09 at 17:35
  • @acidzombie24: If the language objected to ignoring return values it would be a nuisance for [method chaining](https://en.wikipedia.org/wiki/Method_chaining) – Boann Jan 08 '13 at 13:18
9

Yes, allowing overloading on return types complicates a language. It complicates the resolving of overloaded identifiers (e.g. function names). But it isn't impossible, e.g. Haskell allows to overload function based on their return type.

class Num a where
  fromInteger :: Integer -> a 
  ...

Num is a type class in Haskell with a method called fromInteger which is a function from an arbitrary size Integer to an arbitrary type which has an instance of Num. The Haskell type class mechanism is rather different from the class concept of object-oriented languages. Therefore my explanation might sound strange.

But, the result is I can use the function fromInteger and, depending on the calling context, different implementations are chooen at compile time.

There is a whole line of research on type systems, which made this feature possible. Therefore, I'd say it is doable in statically typed languages. Dynamically typed languages would either require time-travelling or some clever ideas.

jmg
  • 7,308
  • 1
  • 18
  • 22
9

To avoid ambiguity.

a( int )
a( bool )
int b()
bool b()

a( b() ) -- Which b?

Here type inference has a cyclic dependency. Which b depends on which a, but which a depends on which b, so it gets stuck.

Disallowing overloading of return types ensures that type inference is acyclic. The return type can always be determined by the parameter types, but the parameter types cannot be determined by the return type, since they may in turn be determined by the parameter types you are trying to find.

Jesse
  • 6,725
  • 5
  • 40
  • 45
  • There are many ambiguous situations, but that wouldn't imply that overloading couldn't be useful if the (1) some "primary" function should be used except in cases where another was clearly superior, and (2) in cases which would still be ambiguous, a compiler could use either. Consider functions (int32,int32)->int32, (int32,int32)->int64, and (int64,int64)->int64. If they yield semantically-identically behavior in cases where the computed value would within an int32, a compiler could use (int64,int64)->int64 always, but performance might be better if (int32,int32)->int32 were used instead. – supercat Aug 29 '11 at 02:14
  • This could be resolved by a hierarchy for types (the overload is picked first for `int`, then ...). This still leaves the problem of user-defined types. In the C++ example, it would probably be best to only allow something like template functions to have overloaded return types, and give a compiler error in the event of ambiguity. The user could get around that by saying `b()` where necessary. – David Stone May 09 '12 at 15:54
  • -1 for mentioning type inference. Well, Haskell has overloading on return types and acyclic type inference. Type inference is complicated because of many things, e.g. loops or recursive dependencies. – jmg Jan 08 '13 at 13:42
6

For a C++ example, consider:

void g(int x);
void g(float x);
g(func());

Which of the overloaded g() functions would be called?

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
  • 1
    in this case it could do what C++ does at times and compiles with an ambiguous error forcing the user to write `g((Float_OR_int_type)func());` –  Oct 20 '09 at 19:14
  • Even that wouldn't help. What if you have multiple methods that return a type that can be validly cast to the object you're casting to? int can be cast to float, and vice-versa. – Herms Oct 20 '09 at 19:20
3

Perl allows a certain degree of return type variation, in that functions can tell what kind of context they're being evaluated in. For instance, a function that might return an array can see that it's running in a scalar context, and just return the putative length directly, sparing the allocation and initialization of the array just to get its length.

Phil Miller
  • 36,389
  • 13
  • 67
  • 90
  • Which works for Perl, but there are several things in Perl that I don't think would work in a more conventional language. Not to mention that Perl doesn't have a type system in the normal sense: types are $scalars, @arrays, #hashes, @typeglobs, and it seems to me I've forgotten one. There is no distinction between integer, float, and string, and operators have to figure out whether to act numerically ($i + 1) or with strings ($i + "1"). – David Thornley Oct 20 '09 at 19:53
  • @David: I don't think I was praising any virtues of Perl in my response, nor suggesting that such things *should* be added to other languages. I was merely answering the part of the question that asks what's out there. – Phil Miller Oct 21 '09 at 02:04
1

Most languages allow for mixed-mode operations with automatic coercion (e.g. float + int), where multiple interpretations are legal. Without coercion, working with multiple numeric types (short, int, long, float, double) would become very cumbersome; with coercion, return-type based disambigation would lead to hard-to-understand code.

mfx
  • 7,168
  • 26
  • 29
1

Allowing these may introduce problems. For example:

int i = func2(func());
int func() { return 5; }
float func() { return 1.3; }
int func2(float a) { return a; }
int func2(int a) { return a; }

This is ambiguous.

amalloy
  • 89,153
  • 8
  • 140
  • 205
Egon
  • 1,705
  • 18
  • 32
  • What would be wrong with specifying that every method signature must have one implementation which is marked as being preferred except when the result of the function was either being assigned to a variable or immediately typecast? – supercat Oct 09 '12 at 23:27