Function, procedure, method, operator...

There is so many terminology which means the same or almost the same. Let’s figure out what is what.

What is common in function, lambda, procedure, routine (subroutine), program (subprogram), application (as a noun), algorithm, method, closure, operator (operation)? All of them are computations.

There are several ways to define computation. For now, I take the simplest: a computation is a sequence of steps, which I call a behavior. There are three common choices for what a step is, leading to three different kinds of behavior:

  • Action Behavior…
  • State Behavior…
  • State-Action Behavior…

Computation and State Machines. Leslie Lamport, 19 April 2008

Function vs procedure

There is a mathematical definition of function (I wrote about it here):

  • takes at least one input
  • produces one output
  • for the same input always produce the same output
  • doesn’t have any side effects - e.g. the only result of executing function is its output nothing else happening

From mathematical PoV, the function is the same as lambda (can be denoted as greek letter λ).

But not all programming language follow the strict definition (I guess for historical reasons). For example, JavaScript:

Same input but different result:

1let counter = 0;
2const increase = (x) => counter += x;
3increase(1) !== increase(1)

Result of the function is not communicated with return value but rather with a side effect:

1console.log('Hello, world!');
2// Outputs Hello, World
3// Returns undefined

No input value:

1Date.now();

This is not a strict rule because we can think about it as:

1Date.now(undefined);

Some programming languages, for example, try to distinguish functions and “none-functions”. For example, Pascal:

A procedure is a routine that does not return a value. A function is a routine that, in contrast to procedures, returns a value. A routine is a re-usable piece of source code that performs some functionality.

The problem is that Pascal doesn’t restrict usage of side-effects inside functions, so division is not precise.

Some programming languages don’t have “none-functions” at all, for example, Haskell. How do they handle side effects then? They use monads to model IO (a subject for a different article).

Routine, program, application

Definition for routine from Pascal:

A routine is a re-usable piece of source code that performs some functionality.

From The C Programming Language:

In C, a function is equivalent to a subroutine or function in Fortran, or a procedure or function in Pascal. A function provides a convenient way to encapsulate some computation, which can then be used without worrying about its implementation.

All of those are the same: routine (subroutine), program (subprogram), application (as a noun) - some re-usable computations.

For example, in Bash, you can treat separate programs (or applications, or binaries, or executables) as “functions” - e.g. take the output from one program and pass (pipe) it to another one.

1cat /var/log/syslog | grep 'post'

cat - concatenate files and print on the standard output. grep - print lines matching a pattern.

Algorithm

Algorithm - a step-by-step procedure for solving a problem or accomplishing some end

Merriam-Webster

In a broad definition, Algorithm is equivalent to computation. Sometimes people refer algorithm as pre-made solutions (in the same vein as design patterns):

Algorithms — methods for solving problems that are suited for computer implementations. Algorithms go hand in hand with data structures — schemes for organizing data

Algorithms, 4th Edition, Robert Sedgewick and Kevin Wayne. A

Compare to:

A design pattern is a three-part rule, which expresses a relation between a certain context, a problem, and a solution. The pattern is, in short, at the same time a thing, …, and the rule which tells us how to create that thing, and when we must create it.

– A Pattern Language: Towns, Buildings, Construction, 1977

Closure and method

Closure is a computation (function) with environment attached to it. For example, in JavaScript:

1const True = (x) => (y) => x;
2const condition = True('then');
3condition('else') === 'then';

condition is the function, with environment attached to it. Environment encolosing condition contains x so it works, but there is not x outside of this closure:

1const NotTrue = (z) => (y) => x;
2const condition = NotTrue('then');
3condition('else'); // Uncaught ReferenceError: x is not defined

The method is a computation (function) with the object attached to it. Typically attached object is accessible through this or self keywords. For example, in JavaScript:

 1class DogClass {
 2  constructor(name) {
 3    this.name = name;
 4  }
 5  bark() {
 6    return `${this.name} is a good dog!`;
 7  }
 8}
 9const belka = new DogClass('Belka');
10belka.bark();

bark is the method.

In some PL passing of object is explicit, for example, in Lua:

1function Account.withdraw (self, v)
2  self.balance = self.balance - v
3end

Operator

In math:

Operator is a symbol or function representing a mathematical operation.

WolframAlpha

In math operator can be treated as a special version of the function, typically operator takes two arguments and uses infix notation (e.g. it is placed between symbols). For example, infix notation:

11 + 1

Prefix notation:

1f(a, b) = a + b
2f(1, 1)

Nobody uses this notation but it would be the same: +(1, 1).

In PL operators can have a different treatment then functions. For example, in JavaScript, there is conditional operator.

1age = 20
2(age >= 18) ? console.log('adult') : console.log('minor');
3// outputs adult
4// returns undefined

If we would want to implement the same thing as a function:

 1const ternary = (condition, thenAction, elseAction) => {
 2  if (condition) {
 3    return thenAction;
 4  } else {
 5    return elseAction;
 6  }
 7}
 8ternary(age >= 18, console.log('adult'), console.log('minor'));
 9// outputs adult
10// outputs minor
11// returns undefined

The problem here is that console.log('adult'), console.log('minor') are executed before passing to ternary function. We can fix it by wrapping actions into functions (() => ...):

1const ternary = (condition, thenAction, elseAction) => {
2  if (condition) {
3    return thenAction();
4  } else {
5    return elseAction();
6  }
7}
8ternary(age >= 18, () => console.log('adult'), () => console.log('minor'));

But as you can see the operator is treated differently than function (in JS).

On the other hand, there are languages which don’t have this problem, for example, in Haskell it would be possible to implement if as function, without need for wrapping in additional function. Because Haskell is doing lazy evaluation aka call-by-need.

In some languages methods and some operators can be the same, for example, in Ruby:

1a + b

Is the same as:

1a.+(b)

It is confusing terminology which varies from one PL to another.

Routine and closure

From compilation point of view sometimes people differentiate routines (think of C functions) and closures. Because routine can be implemented as a trivial pointer, but closure compilation would need more work.

Except where otherwise noted, content on this site is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0