Skip to main content

What makes mathematica a "functional programming language"?


According to the website of mathematica itself, mathematica is a "functional progrmaming language".


I am trying to understand what "functional programming" is. I don't think I have ever worked with "functional programming" before (though I'm not sure, since I don't quite know what it is). I've been taught programming mainly in terms of object oriented programming.


But here are some reasons why I don't see why mathematica is a functional programming language:




  1. I just read that functional programming languages have "immutable variables" much as variables in mathematics are immutable. However, the variables in mathematica can be changed arbitrarily





  2. I just read that in functional programming languages, the programmer "gives up control over the order of execution". However, in mathematica, we can control this order, to perfect detail, by changing the order of the expressions.




  3. "non-functional languages" like C++ also contain functions. If we made a C++ program that consisted only of one object, and simply wrote the program on the basis of functions, how would mathematica be fundamentally different from this?





Answer



On the Wolfram web site there is a description of the Wolfram Language (WL) entitled Notes for Programming Language Experts. At time of writing, it lists 47 attributes of WL including Functional. It also lists a number of other programming paradigms such as Symbolic, Declarative, Procedural, Concatenative, and Query Capable. (It also lists Object-Oriented, but only as a contrast to WL's "symbolic alternative to traditional object-oriented programming").



This characterization emphasizes that WL is a multi-paradigm language. The native paradigm is based upon pattern-based transformations of expressions. As we will see, this can ably simulate the Functional paradigm.


What is Functional Programming?


The accepted view as to what constitutes functional programming has evolved over time and is somewhat contentious. However, I think it is safe to say that there is one functional programming feature upon which all commentators would agree: the use of higher order functions.


Higher Order Functions


A function is "higher order" if it takes one or more other functions as arguments. Let us look at an example. We will start with a list of numbers:


{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

If we want to apply the function f to those numbers, we could write:


{f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9], f[10]}


but this is repetitive and tedious. We would like a way, in some sense, to "multiply together" the list and the function f. That is, we wish to treat f like a value in some kind of combining operation with the list value. In Wolfram Language, that operation is called Map:


Map[f, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}]
(* {f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9], f[10]} *)

which can also be written as:


f /@ {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}

/@ looks like an arithmetic operator that is being applied to a function value and a list value. Since at least one argument is a function, /@ is a higher order function.


But there is another, "more pure", way to perform mappings like this. We can convert f from a function that operates upon single values to one that operates upon a list:


g = Map[f];


g[{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}]
(* {f[1], f[2], f[3], f[4], f[5], f[6], f[7], f[8], f[9], f[10]} *)

Here we have used the so-called operator form or curried form of Map. This is kind of function transformation sometimes called "lifting", in that the type of a function's input argument has been lifted from one domain to another.


It is possible to perform "function arithmetic" and never mention any function arguments. Consider the following function definition which determines whether the lengths of the strings in a list are all odd:


allOddLength = Apply[And] @* Map[OddQ @* StringLength];

allOddLength[{"one", "is", "even"}]
(* False *)


allOddLength[{"all", "are", "odd"}]
(* True *)

Note how allOddLength is defined purely by means of function algebra. In the functional world this is often termed purely functional. Unfortunately this label conflicts with the WL jargon term pure function. Take care to distinguish between the two senses of "pure".


We can see from these examples that code in the functional style is easily expressed in WL. On this basis, I think it is safe to say that WL has good support for functional programming.


Other Functional Programming Features


We noted earlier that the exact definition of functional programming is contentious. Paul Hudak wrote an excellent survey paper on Conception, evolution, and application of functional programming languages. In it, he proposes five distinguishing features of modern functional programming languages:



  1. Higher Order Functions


  2. Non Strict Semantics (Lazy Evaluating)

  3. Data Abstraction

  4. Equations and Pattern Matching

  5. Formal Semantics


Today, the term "purely functional" may imply any or all of these features, depending upon vintage and nature of the context within which the term is used.


WL, as Mathematica, was born in the 1980's where "functional" was still popularly equated with the higher-order function feature which we have discussed at length. Nevertheless, let us look at the other features (but only briefly as each topic could easily balloon into a further wall of text).


Non Strict Semantics (Lazy Evaluating)


WL offers many constructions that permit lazy evaluation of expressions, but they require manual intervention by the programmer. They are not implicit and pervasive as they are, say, in Haskell. For an introduction to evaluation control in WL, see Non-Standard Evaluation. For an example of its use in a quasi-functional style, see Functional style using lazy lists?


Data Abstraction



WL's symbolic nature and use of expressions and transformations allows for very strong data abstraction capabilities. For example, here are three distinct implementations of a lookup table:


lt1[keys_, values_][key_] := First@Pick[values, keys, key]
lt2[keys_, values_] := AssociationThread[keys, values]
lt3[keys_, values_] := With[{rules = MapThread[Rule, {keys, values}]}, # /. rules &]

They all present the same interface but their distinctive data representations and operation algorithms are hidden from callers. It is true that this kind of abstraction is idiomatic rather than a built-in concept, but I would argue that the distinction is largely moot.


Hudak's paper regards strong-typing to be part of the data abstraction feature. In this regard, WL is lacking. Except for the small language subset that can be compiled, there is no built-in provision for strong typing (at least in the present version 11).


Equations and Pattern Matching


This, of course, is the bread and butter of WL. There is a conceptual disconnect between the notion of equational reasoning as described in the paper between equation solving in WL. Equational reasoning is meant to be implicit and pervasive where as WL's equation solving is essentially a particular set of run-time library functions. Nevertheless, I would score WL well in this category on the strength of its pattern-matching alone.


Formal Semantics



This is an area where WL is sorely lacking. The idiomatic nature of WL is simultaneously a strength and weakness. The strength is that it allows considerable notational flexibility. The weakness is that the semantics are sometimes ill-defined.


Conclusion


Even though it is idiomatic, WL's support for higher-level functions is very strong. On this basis alone, I believe that WL qualifies as supporting the functional programming paradigm.


If we consider the wider criteria that modern commentators often include in their definitions of functional programming, then WL still fares well for many of those features.


Comments

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Adding a thick curve to a regionplot

Suppose we have the following simple RegionPlot: f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}] Now I'm trying to change the curve defined by $y=g[x]$ into a thick black curve, while leaving all other boundaries in the plot unchanged. I've tried adding the region $y=g[x]$ and playing with the plotstyle, which didn't work, and I've tried BoundaryStyle, which changed all the boundaries in the plot. Now I'm kinda out of ideas... Any help would be appreciated! Answer With f[x_] := 1 - x^2 g[x_] := 1 - 0.5 x^2 You can use Epilog to add the thick line: RegionPlot[{y < f[x], f[x] < y < g[x], y > g[x]}, {x, 0, 2}, {y, 0, 2}, PlotPoints -> 50, Epilog -> (Plot[g[x], {x, 0, 2}, PlotStyle -> {Black, Thick}][[1]]), PlotStyle -> {Directive[Yellow, Opacity[0.4]], Directive[Pink, Opacity[0.4]],