Total functional programming (also known as strong functional programming,[1] to be contrasted with ordinary, or weak functional programming) is a programming paradigm that restricts the range of programs to those that are provably terminating.[2]
Termination is guaranteed by the following restrictions:
These restrictions mean that total functional programming is not Turing-complete. However, the set of algorithms that can be used is still huge. For example, any algorithm for which an asymptotic upper bound can be calculated (by a program that itself only uses Walther recursion) can be trivially transformed into a provably-terminating function by using the upper bound as an extra argument decremented on each iteration or recursion.
For example, quicksort is not trivially shown to be substructural recursive, but it only recurs to a maximum depth of the length of the vector (worst-case time complexity O(n2)). A quicksort implementation on lists (which would be rejected by a substructural recursive checker) is, using Haskell:
import Data.List (partition) qsort [] = [] qsort [a] = [a] qsort (a:as) = let (lesser, greater) = partition (<a) as in qsort lesser ++ [a] ++ qsort greater
To make it substructural recursive using the length of the vector as a limit, we could do:
import Data.List (partition) qsort x = qsortSub x x -- minimum case qsortSub [] as = as -- shows termination -- standard qsort cases qsortSub (l:ls) [] = [] -- nonrecursive, so accepted qsortSub (l:ls) [a] = [a] -- nonrecursive, so accepted qsortSub (l:ls) (a:as) = let (lesser, greater) = partition (<a) as -- recursive, but recurs on ls, which is a substructure of -- its first input. in qsortSub ls lesser ++ [a] ++ qsortSub ls greater
Some classes of algorithms have no theoretical upper bound but do have a practical upper bound (for example, some heuristic-based algorithms can be programmed to "give up" after so many recursions, also ensuring termination).
Another outcome of total functional programming is that both strict evaluation and lazy evaluation result in the same behaviour, in principle; however, one or the other may still be preferable (or even required) for performance reasons.[4]
In total functional programming, a distinction is made between data and codata—the former is finitary, while the latter is potentially infinite. Such potentially infinite data structures are used for applications such as I/O. Using codata entails the usage of such operations as corecursion. However, it is possible to do I/O in a total functional programming language (with dependent types) also without codata.[5]
Both Epigram and Charity could be considered total functional programming languages, even though they do not work in the way Turner specifies in his paper. So could programming directly in plain System F, in Martin-Löf type theory or the Calculus of Constructions.
Original source: https://en.wikipedia.org/wiki/Total functional programming.
Read more |