Question

Why do some languages differentiate between methods that return a value and methods that don't?

i.e. in Oracle's PL/SQL, where the primary difference between a function and a procedure is that the function must return a value, and the procedure must not.

Likewise for languages that don't, why not?


EDIT: I have found a related question that might interest people reading this question:

Was it helpful?

Solution

Because in the original conceptions of Computer Science theory and practice, Functions and Subroutines had virtually nothing to do with each other.

FORTRAN is usually credited as the first language that implemented both of these and demonstrated the distinctions. (Early LISP had a somewhat opposing role in this also, but it had little impact outside of academia).

Following from the traditions of mathematics (which CS was still part of in the 60's) functions were only seen as the encapsulation of parametrized mathematical calculations solely intended to return a value into a larger expression. That you could call it "bare" (F = AZIMUTH(SECONDS)) was merely a trivial use case.

Subroutines, on the other hand were seen as a way to name a group of statements meant to have some effect. Parameters were a huge boost to their usability and the only reason that they were allowed to return modified parameter values was so that they could report their status without having to rely on global variables.

So, they really had no conceptual connection, other than encapsulation and parameters.

The real question, is: "How did so many developers come to see them as the same?"

And the answer to that is C.

When K+R originally designed their high-level macro assembler type language for the PDP-11 (may have started on the PDP-8?), they had no delusions of hardware independence. Virtually every "unique" feature of the language was a reflection of the PDP machine language and architecture (see i++ and --i). One of these was the realization the functions and subroutines could be (and always was) implemented identically in the PDP except that the caller just ignored the return value (in R0 [, R1]) for subroutines.

Thus was born the void pointer, and after the C language had taken over the whole world of programming, the misperception that this HW/OS implementation artifact (though true on almost every subsequent platform) was the same as the language semantics.

OTHER TIPS

In a pure or effect-typed setting there is a world of difference, because obviously methods that "don't return anything" are only useful for their side effects.

This is analogous to the distinction between expressions and statements, which can declutter a language and eliminate a class of usually-mistaken programs (which, of course, is why C doesn't do it ;)).

To give one tiny example, when you distinguish clearly between expressions and statements, if(x = 3), as opposed to if(x == 3) is syntactically incorrect (for using a statement where an expression was expected) and not merely a type error (for using an integer where a boolean was expected). This has the benefit of also disallowing if(x = true) which would be permitted by a type-based rule in a context where assignments are expressions which have the value of their right operand.

In a language which encapsulates effects with monads, the important distinction becomes the one between:

  • functions that return () which are pure functions and can only return one useless empty value called () or diverge
  • functions that return IO () (or unit in some other monad) which are functions with no "result" except effects in the IO (or whichever) monad

Excuse me answering a two year old question, especially with something unique to my own language Felix http://felix-lang.org but here goes anyhow :)

In Felix, functions and procedures are fundamental different, and it isn't just that procedures have side effects and are called in statements, whereas functions don't have side effects and are used in expressions (because Felix also has generators which are functions with side-effects .. :)

No, the execution model is fundamentally different, primarily for performance reasons, but not entirely. The model is:

  • Functions put their return address on the machine stack, and the return value too.
  • Procedures use a linked list on the heap. Procedural code is flat, it does not use the machine stack.

This is typically inefficient, so why do it? The answer is: Felix procedure are all potentially co-routines (fibres). They can switch control to another procedure by accessing a channel. This causes an exchange of control.

  • For performance reasons copying the machine stack on control exchange is not an option.
  • For memory management reasons swapping stack pointers is not an option either.

The OS typically swaps stack pointers for threads, which is reasonably fast, but has a fundamental problem on linear address machines: you either have to limit the maximum size of the stack to a ridiculously small value, or limit the number of threads to a ridiculously small value. On a 32 bit machine, there is not enough address space to even contemplate this solution. On a 64 bit machine, stack swapping has more potential, but of course user demands always grow to outstrip hardware 3 days after it is released .. :)

Felix just swaps a single pointer to the heap-based stacks, so context switches are blindingly fast and very little address space is wasted. Of course the cost is heap allocations on procedure calls.

In the compiler, a lot of the architecture of the theoretical model is optimised away on an "as-if" basis, so actual performance and implementation can be quite different to the theoretical model, provided the compiler can prove that you can't tell the difference .. other than being denied the opportunity to make a cup of coffee with leisure :)

So here, you have a different answer as to why functions and procedures might be treated differently.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top