Is it possible to free von Neumann programming style?(John Backus).
"What was new and essential to all mathematics was the entirely general conception of function" (Dedekind).
The Functional Paradigm
The functional paradigm of programming is based on using a single concept (or conceptual primitive): the function, in the mathematical sense of the term. It was born as a reaction to the imperative paradigm. John Backus has been one of the prime movers of functional programming semantics, criticizing the concept of variables and the concept of assignment statements, claiming that they are the root of many evils and a "curse" transmitted by the von Neumann model [Backus, 1978].
Features of the functional paradigm
Uniformity.
Everything is a function. In functional (also called applicative) languages, all constructs are functions. You "apply" input data (function arguments) to a function to obtain a result (calculated by the function), which depends only on those arguments. There is no side effect: a function has no effect other than producing the result from the arguments.
Referential transparency.
In general, referential transparency refers to the fact that, in a functional-type program, the semantics of an expression is always the same, regardless of its place within the program. That is, the evaluation of the expression always produces the same result. This does not occur with imperative programming, since the statements of assignment of values to variables, makes that the same expression does not mean the same thing in different points of the program.
The advantages of referential transparency are:
Modifications made to one function do not affect the rest of the functions.
They facilitate the verification of programs, i.e., the mathematical demonstration that they meet the specification.
Specification.
Functional languages make it possible to specify the processes or transformations to be performed with the data without the need for implementing references. When designing a program, the what to do is abstracted before specifying how it should be done. Functions are the vehicles of this first type of abstraction.
Hierarchy.
The argument of a function can be another function. In this way, higher-order functions are constructed. A program is also a function that is at the top of the hierarchy of functions used.
Recursion.
Functions can call themselves, which greatly simplifies programming.
Polymorphism.
This is a feature supported by many functional languages. A polymorphic function accepts parameters of different data types. This flexibility allows you to build generic or high-level functions, without the need to create different versions of functions for each of the data types.
Limitations of the functional paradigm
The exclusive application of the functional paradigm leads to serious expressive difficulties:
The emphasis is on functions, i.e., on data processes, and on the process of functional decomposition. Data have a secondary role and emerge during these processes.
In-place updates of data structures are not possible.
Data structures have to be fully built each time, even if only one element has been modified. An attempt to avoid this are so-called "functional data structures" considered as updateable objects, accessible by language functions [Milewski, 1990].
Poverty of data structures.
Most functional languages use the list as the only or most important data structure.
No assignment statement or global variables.
Although both can be emulated by function parameters, the truth is that this is a severe expression limitation. This is why some functional languages such as Lisp incorporate them.
Absence of internal states.
This is a consequence of the lack of side effects. The behavior of a function is always the same with the same input arguments. This implies difficulty in modeling the real world, where behavior varies depending on the internal state of objects.
Specification in MENTAL
Function definition
A function f is an expression dependent on one or more parameters that, when evaluated with the same arguments always produces the same result.
All parameters are input. Parameters are expressions.
There is only one output, which is the result of the function, which is an expression. This output expression can be of any type and as complex as you want.
Ways to specify and evaluate functions
There are many ways to specify and evaluate functions: directly (unnamed), named, with and without formal parameters, with or without deferred evaluation, with absolute or relative substitutions, with partial or total evaluation, etc.
By means of a direct (unnamed) expression with relative arguments:
(x+y+3)/(y=1) // ev. x+4
(x+y+3)/{x=1 y=2} // ev. 6
By means of a substitution expression, without formal parameters, with the arguments specified absolutely (inline):
(z = (x+y+3)°) // ev. (z = x+y+3)
(y = 1)
z // ev. x+4 (partial evaluation)
(x=1 y=2)
z // ev. 6 (total evaluation)
By means of a substitution expression, without formal parameters and with relative substitution:
( z = (x+y+3)° )
z/(y = 1) // ev. x+4 (partial evaluation).
z/((x = 1) (y = 2)) // ev. 6 (total evaluation)
By means of a substitution expression with formal parameters, mixfixed form in general:
〈( f(x y) = (x+yx*y) )〉
f(3 4) // ev. (7 12)
〈( (a x b y) = (a ← x<y →' b) )〉
(a 3 b 4) // ev. a
(a 4 b 3) // ev. b
By means of a substitution expression, with or without formal parameters, that produces a result resulting from a process:
( z = (( (i = 1) (i = i*2)★n ¡i )!)° )
z/(n = 4) // ev. 16
Conversion of a sequence x of binary digits to a decimal number.
The procedure consists of multiplying the first digit by 2 and adding the second, multiplying the result by 2 and adding the third, etc., until the last digit is added. For example, 1011 = ((1*2 + 0)*2 + 1)*2 + 1.
Conversion of a positive integer n to a sequence of binary digits.
The procedure consists of dividing the number n by 2, the quotient is divided again by 2, etc. until the quotient is 1. Then the last quotient is taken and all remainders are taken in reverse order. Example for the number 19:
Oper.
Quotient
Rest
Result
19÷2
9
1←
10011
9÷2
4
1←
4÷2
2
0←
2÷2
1←
0←
〈( decbin(n) = ( (s = ()) // initial result sequence
(m = n)
( (c = m÷2)) // quotient
(c=1 → ¡(c s↓))
(r = remainder(m 2)) // remainder
// include remainder at the beginning of the sequence
(s = (r s↓))
(m = (m = c) )★ )! )〉
decbin(19) // ev. (1 0 0 0 1 1 1)
Function that returns "S" if n is prime and "N" otherwise.
These are functions that have as functions as arguments and return another function as a result. In general, a parametric expression can be passed as an argument to a function. Examples:
Note that the function parameters have to indicate the type of elements to which they apply (integers, in this case).
Recursive application of a function
The recursive application (n times) of a function f(x), with argument a, that is, for example, with n=4: f(f(f(f(f(a)))) can be specified by (f★n a)Δ being Δ the hierarchical binary grouping operator, in this case to the right. Recall that, for example,
(f f f f a)Δ = f(f(f(f a))
If the function name is fixed and the argument a as well, you could define the one-parameter function:
〈( z(n) = (f★n a)Δ )〉
Example:
〈( f(x y) = (x+yx*y) )〉
〈( z(n) = (f★n (a b))Δ )〉
z(1) // ev. (f (a b)) eq. f(a b) ev. (a+b a*b)
z(2) // ev. (f (f (f (a b)))) eq. f(f(f(a b)) // ev. ((a+b)+(a*b) (a+b)*(a*b))
If the x argument were variable, we could also define the two-parameter function:
〈( z(x n) = (f★nx)Δ )〉
Example:
〈( f(x y) = (x+yx*y) )〉
z((a b) 1) // ev. (f (a b)) eq. f(a b) ev. (a+b a*b)
z((a b) 2) // ev. (f (f (f (a b))) eq. f(f(f(a b))) // ev. ((a+b)+(a*b) (a+b)*(a*b))
Maximum generality is achieved when the function itself is also a parameter:
〈( z(f x n) = ((f★nx)Δ)/f )〉
Example:
z(〈( f(x y) = (x+yx*y) )〉 (a b) 1) // ev. (f (a b))/〈( f(x y) = (x+y x*y) )〉 ev. (a+b a*b)
z(〈( f(x y) = (x+yx*y) )〉 (a b) 2) // ev. (f (f (f (a b))))/〈( f(x y) = (x+yx*y) )〉 ev. f(f(f(a b))) ev. ((a+b)+(a*b) (a+b)*(a*b)))
Partial evaluation of functions
This is to supply fewer arguments than parameters the function has defined. This allows you to define particular functions, instantiated from more general functions. Example:
In MENTAL, the partial evaluation mechanism is generic, i.e., it is applicable to all types of expressions [see MENTAL Language - Techniques - Partial Evaluation].
Bibliography
Backus, John. Can Programming be liberated from the Von Neumann stile? A functional style and its algebra of programs. CACM, vol. 21, pp. 613-641, Ag. 1978.
Cousineau, Guy; Mauny, Michel. The Functional Approach to Programming. Cambridge University Press
Hudak, P. Conception, evolution and application of functional programming languages. ACM Computing Surveys, 21 (3), pp. 359-411, Sep. 1989.
Jones, S.L. Peyton. The implementation of functional programming. Prentice-Hall, Englewood Cliffs, NJ, 1987.
MacLennan, Bruce J. Functional Programming: Practice and Theory. Addison-Wesley Professional, 1990.
Michaelson, Greg. An introduction to Functional Programming Through Lambda Calculus. Dover, 2011.
Milewski, Jaroslaw. Functional Data Structures as Updatable Objects. IEEE Transactions on Software Engineering, vol. 16, no. 12, pp. 1427-1432, Dec. 1990.
Turner, J.A. An overview of Miranda. ACM SIGPLAN Notices, 21 (12), pp. 158-166, Dic 1986.