Software Development

Correcting The Future

Code Reads: Backus' 1977 Turing Lecture

This is my review of this paper[pdf] from Scott Rosenberg's Code Reads. I'm posting my comments now instead of waiting for Scott's comments as my Internet access may be sporadic in the near future.

As you've probably heard, John Backus has died at the age of 82. You may know him from his contributions such as BNF and the FORTRAN language.

WARNING: With all due respect to Backus, I am not going to sugar coat this assessment of his paper. So if you have close ties to Backus, I suggest you read something else. Actually, I demand that you not read this.

I have read the paper in its entirety. I understood everything in it and found it rather dry. I'll go through a few sections just to give a feeling of how this paper reads.

2.2.2 Applicative models.
Examples: Church's lambda calculus, Curry's system of combinators, pure Lisp, functional programming systems described in this paper. Foundations: concise and useful. History sensitivity: no storage, not history sensitive. Semantics: reduction semantics, no states. Program clarity: programs can be clear and conceptually useful.

2.2.3 Von Neumann models. Examples: von Neumann computers, conventional programming languages. Foundations: complex, bulky, not useful. History sensitivity: have storage, are history sensitive. Semantics: state transition with complex states. Program clarity: programs can be moderately clear, are not very useful conceptually.



The paper gets quite repetitive in this respect. Von Neumann bad! Functional GOOD! It goes on in caveman style narrative until we reach the section where quite unnecessary complex equations are found. These are somehow deemed simpler than von Neumann styled languages. The abrupt transition into hieroglyphs that only serve to confuse is rather curious considering the extensive sections devoted to how simple pure functional programming is supposed to be.

The repetition makes one wonder why this was necessary. Did Backup not feel that the work could speak for itself? I suppose that at the time, Backus may not have been subjected to infomercials, but I am sure that he would have found a home quite easily in this area.

Never was there any reasoning as to why von Neumann models are bad and why functional (or what Backus calls applicative) models are good. He says they are. But I found nothing convincing other than his insistence that the bus between data and the CPU is a bottleneck. This is what he calls the word-at-a-time bottleneck.

Von Neumann machines have a data store where the CPU can modify this data one item at a time. With functional programming, you have functions and reductions. There is never any named data or external entity that can be modified. There are apparently no states (more on this later).

The absence of full scale, effective programming styles founded on non-von Neumann principles has deprived designers of an intellectual foundation for new computer architectures.



On and on, it goes like this. Even the attempts at giving a reason are just rehashes of the same tired unfounded assumptions.

I agree that the von Neumann model is perhaps not the best. I have used many of Backus' contributions and am grateful for them. I was really optimistic as I started to read this paper. I wanted to agree with him and I would have let some items slide if there was some attempt at minimal logic or reasoning behind the arguments. I so longed to have one of the greats teach me something new or just appreciate the knowledge that was handed down at the time. But no. This paper is nothing more than an infomercial.

But the complacent acceptance most of us give to these enormous, weak languages has puzzled and disturbed me for a long time.



Baseless accusations and insulting those who use imperative and OO languages all in one sentence. Not bad. Looks like I can learn something after all. This statement also says that he does not understand why people use imperative (and OO) languages. So there's something Backus doesn't get. It should be noted that instead of looking at why these people use these languages, he decided that there was nothing to learn here. I could say the exact same comment about anything. Doesn't mean I'm right. So I find the above comment rather strange considering the circumstances. Though I'm not sure how this paper was created. Was it picked after the awards? Or was it written afterwards? In any case, such written language should not find itself in any respectable published paper no matter who wrote it.

The funniest thing is after reading this paper and how simple he claimed the functional model was, I longed for the for loop presented at the top of the paper:

c := 0
for i := 1 step 1 until n do
  c := c + a[i] * b[i]


Call me crazy, but that's as simple as it gets. Apparently, I'm wrong. Makes me wonder where FORTRAN came from in the first place if Backus had these opinions.

All right. That about sums up the paper. But there's more under the surface. It shows a basic lack of understanding of computers by most researchers. I've been searching for a long time why programming languages were so low level. I think this paper demonstrates more about this stagnation than anything else.

First, there is a serious lack of understanding as to what a function is. These researchers seem to be proponents of mathematical constructs, yet they fail completely when attempting to apply these principles to computers. This isn't targeted at Backus, but to all computer researchers who deal in programming languages. They just don't get it.

In many papers, including this one, they confuse the properties of a function. These papers all trip over themselves as they try to make a point that is impossible to make. Backus himself erroneously states that the substitution operation yields near unlimited power. The confusion lies in the fact that they think that a function can both produce an output and at the same time be reduced or substituted. I understand what they mean, but they don't see a distinction between how a function acts and the more generic consequences of producing an output.

A function seems to take an input and produce an output. It looks that way, but it's not entirely true. It has very definitive restrictions. Let's say I had an 'entity' that took an input and produced an output. For example, a post office. I deliver a letter to the post office (as input) and the post office stamps it (processing) and sends it elsewhere as its output. See how this differs from a function. A function REQUIRES that the output be sent back to the provider of the input. But in general, something that takes an input and produces an output has no such requirement as can be seen with the post office example. Do you understand that? Backus and others do not. Researchers do not understand this. Math only works with the return to sender requirement. That is NOT enough.

Returning to the sender is a critical property of reduction. It's also what makes possible inlining of functions. The arguments and return values are 'sockets' or 'terminals' to be linked in to another function. Placeholders if you will. But both the arguments and return values must be present (and linked) within the same calling or containing function. As such, it uses a restrictive form of input and output. One where it almost should not be called input and output at all because you cannot send the output to a different location than where the input came from. A function is nothing more than a group of operations that can be reused. Hey, great. You can reuse. But pretending that functions are not restrictive forms of input/ouput processing is very dangerous and why all these papers all fail and trip over themselves.

In computers, imperative programming is able to take inputs from the data store. A device can place data there (such as HD via DMA). Then your software (triggered directly or indirectly by interrupts) can take that data and transfer it to something else such as your video or audio card. This imperative software has input and output. But it doesn't send its output to the same device that produced the input. It can if it wants to and if the device supports it. But herein lies the flexibility. That's where imperative programming has a nice balance. It is internally restricted via function calls to return to the sender, but via state, it can escape this restriction. The functional model has no such balance. A functional model, by itself, cannot exist. A substitution model, when applied to its fullest, will end up with a single entity. Without interaction, it may as well not exist.

Object Oriented programming suffers from the same problems. If you ask an object to do something, there's no reason that function couldn't just be inlined inside the caller. If many objects call the same object (even with different functions), the local data binds all these calling objects together. It gets messy because we no longer have a clean model of input/processing/output. We have many return to sender functions using the same object data. The point of storage was to provide the input/processing/output model where the input and output were not the same entity. But if this data is locked into an object, the purpose seems rather moot. You could use it to transfer data from one called function to another later called function. However, the nature of a single CPU renders this a dubious strategy as well.

Both functional and OO have never understood what a function actually is. More so, it has never respected the properties of a function and what purpose storage is supposed to play in software.

We need both reductions and input/output processing where the output need not be sent to the provider of the input. BOTH! Functional has only reductions. And OO, well, it just warped the intention of storage. Merging the two would not help though. There's still denial about what the execution point is doing.

Returning to an earlier topic, functional programs do have state. I don't care how many times I hear someone say it doesn't, it's just not true. Functional programming is where state is always coupled to execution (or its equivalent reduction model). But this does not mean that there is no state. What they mean is there is no uncoupled state. The only way you can uncouple this state is by applying its associated function otherwise known as a reduction. This is how you change state.

Math is fine, but it's only one side of the equation. The other side is interaction. This is why most functional languages are not pure. So they can push the functional paradigm all they want, but these languages will never be pure. They can't be. I have yet to see a single paper that understands this duality between modularity and interaction. Without it, all papers fail.

The reason people use imperative and OO languages should be clear. Even if OO is a little warped, these languages provide a mechanism to unbind itself from the return to sender paradigm when it makes sense to do so. Imperative languages still have the best balance. Data flow would be optimal if there was an available framework that didn't require the execution point.

Also, interaction isn't between TWO entities, but THREE different entities! One entity produces the input, another processes it, and a final one takes that output. In this way, you can connect and produce networks of infinite size and complexity. With today's notion of programming, which hasn't changed in 50 years, we can't even attempt to get there. So there are plenty of unnecessary difficulties that make our software less than optimal right from the start. We cannot talk about improving software until we understand the properties of our tools and accept them at face value.

Recent papers offer no better analysis of the true properties of a software model. I hope that future papers can improve and correct the work of the past. Improvement is at the heart of all work, whether in computers or any other field. I am saddened that there is much political influence in the computing field. I am further saddened that there are still some that do not understand that math is only half the equation and that this pursuit will doom us all. However, I am hopeful that progress will be made. I am confident that we will turn a new page in computing very soon. Not by the researchers, but by the users. By the very people who get things done and whose only motivations are to fulfill this goal.

Going Offline for a WhileWhy the Black Box Analogy Does Not Work for Functions

Comments

Unregistered user Thursday, March 22, 2007 12:40:59 AM

Nate writes: "The confusion lies in the fact that they think that a function can both produce an output and at the same time be reduced or substituted." Hmm. As I understand it, a function as a pure mathematical construct is just a one-way relation mapping one set of values to another. I visualise it as a box or a chip with one input pipe and one output pipe. Every input must generate the same output, but there's no guarantee that you can run it in reverse; some input values may map to the same output value. So it's a lossy transformation. It throws data away. Which is what, eg, addition does: it takes a pair of numbers (one input), and spits out a number (one output). You can't un-add a number and find out its component parts. There's nothing in the mathematical idea of 'function' which says 'it must return output back to its caller'. The input pipe and output pipe are separate and they're not 'hooked up' to anything in particular. I can easily see how one could build a model that was a bunch of functions wired directly together. I think that the 'recursive call and return' model of function *evaluation* is just one way of handling them, and looks to me like a simple shortcut that's good for some applications. Specifically, it looks to me like a 'star' or 'router' topology. IE: if a bunch of functions that you want to wire together are like chips on a circuit board, each has an input and an output cable, you could either wire them together separately, or you could wire them to a central hub. Then each one would have both its input and output wires going back to that hub, and the hub would do the routing. Which is a lot easier to build and maintain in many cases - the phone system, for instance, and the lower layers of the Internet (where you have just one ISP). The 'recursive call and return' evaluation model seems to be very similar to an implementation of this kind of centralised routing in software. Two functions communicate using a third which sits 'above' them and does the call/return - it's being the 'hub'. Doesn't mean though that you couldn't imagine other ways of doing it. It seems to me that a 'thread of execution' is much the same idea as a single signal or message being emitted from one component; waking up all the components it touches which otherwise 'sleep' to save burning CPU power. Which suggests that if you route the output of a component to multiple places, each of these should (at least briefly) automatically spawn its own automatic 'thread' to model the parallel signal paths. Similarly you'd need a component to somehow implement a 'wait' or 'match' or 'synchronise' feature to pull together input signals from multiple sources and make sure they belong together. The other thing though is that since functions are only one-way transformations, I'm not convinced that you can model all data with them. I think generalised relations are needed to describe, eg, databases and semantic webs, and these may have interesting properties related to parallel programming.

Vorlath Thursday, March 22, 2007 1:24:11 AM

The output of a function MUST be returned to the originator of the input. If you produced a function that didn't have this requirement, then you could still call it a function if you wish (I wouldn't), but it would be drastically different than what is in use today. The failure in computing is not being to distinguish this critical difference.

The mathematical view does necessitate the return to sender property in order to be reducible(sp?). If the output is sent elsewhere directly, then it chains two other entities and negates any possibility of being reduced.

Functions in functional languages and imperative languages are two connections on the same side of the box, each flowing in opposite directions. If any connections appear on the other side, it is used to call or reduce sub-functions. Assuming left to right, the left side can only have two connections while the right side can have many pairs of connections. However, there must be a point where a leaf box will have no connections on one side (right) of the box. If all boxes are taken together, you still end up with two connections on the same side of this container box. This process is called reducing. This reduction is impossible with one connection on each side where each one is connected to a different other black box.

The often-used black box with one connection on each side is erroneous for the common function. It is not a correct description. The other scenarios you describe are drastically different and more powerful that the common function. The hub scenario is one such construct that does not require return to sender even though you usually have bi-directional connections.

If this difference were recognised, I believe we would start to move forward again in computing.

Vorlath Thursday, March 22, 2007 1:25:35 AM

I should perhaps make some drawings of these boxes.

Unregistered user Monday, March 26, 2007 5:31:46 AM

Nate writes: "The output of a function MUST be returned to the originator of the input. If you produced a function that didn't have this requirement, then you could still call it a function if you wish (I wouldn't), but it would be drastically different than what is in use today." If something is a function in the *mathematical* sense, it's always true to call it that. A mathematical function is a *relationship*. A mathmatical function is not an embodiment of *a mechanism for mechanically evaluating* the output of a function given its input (and carried out, in one such embodiment, by sequential variable substitution, recursive evaluation of subfunctions, and rewriting of the result), which is what the thing often called a "function" in *computing* is. Very different beasties. It's unfortunate that terminology has been forked between the two fields to the point where we *call* a 'function' (definition 2) to *evaluate* a 'function' (definition 1), but there you are.

Vorlath Monday, March 26, 2007 5:34:34 PM

There is no difference between calling a function and evaluating a function (other than low level details). In both cases, the result takes the place of the function in the original equation (or function). Use whatever mechanism you want and this will always hold true.

Your two definitions are one and the same. Where is the difference? There is none.

However, I can show you another form of relationship that is much more powerful. How can I formulate an equation where the evaluated result is used somewhere other than the location of the original pre-evaluated function.

int f(x)
{
  return g(x);
}


How do I make the evaluation of g(x) be sent to something other than returning to f(x)? You can't.

In other words, how do I get inputs from one equation [f(x)] and use the output or evaluations in another [h(y) where y would be the evaluation of g(x) directly]? Math doesn't take this into account. Mathematicians think functions are the best (and maybe only) way to evaluate things. This is incorrect. Functions are actually a very restrictive form of evaluation. The more flexible version just isn't very easy to write down on paper.

Mathematicions use one source for everyhting. They would change my function to something like h(g(x)), but I need x to come from f(x). So they'd do h(g(f(x))), but this doesn't work as the body of f(x) IS g(x). This would be recursive. With math, I cannot maintain all requirements. It may seem like it's unecessary. And for math, it is not needed because there's only ever one person doing the evaluation. But without interaction, your software is useless. Math is not good enough for this. If you wish to evaluate multiple things at once, you can do this:

x -> f -> g -> h -> output

f, g and h can process multiple data sets at once. With math alone, my requirements are impossible and deemed unnecessary. With I/O components, it's easy and necessary because interactions are necessary.

Computers are about interactions, not computations. Not understanding this is the biggest mistake people make in writing compilers and software alike. Computions that never reach human beings may as well not exist. If I put more computable power than the world has ever seen in a forest without human interaction, it may as well not exist (even with working software). Math, computations and evaluations by themselves are useless. Functions make interactions impossible.

So a function is not just a relationship. It also holds a requirement that it must stay where it is. An evaluation can replace a used function, but only where it is located. This is what makes substitution possible. This is has severe consequences.

Unregistered user Monday, April 2, 2007 7:13:45 AM

Anonymous writes: The fact that purely functional languages are unsuitable for writing real applications will come as a great shock to the people using them for such purposes. :)

Unregistered user Monday, April 2, 2007 7:36:18 AM

Anonymous writes: In your "as simple as it gets" examples, what is the value of n? Does it even work?

Unregistered user Monday, April 2, 2007 11:51:47 AM

Anonymous writes: Using goto carefully was a step in the right direction for managing instruction-flow. Using side-effects carefully is a step in the right direction for managing data-flow. Yay Haskell.

Unregistered user Monday, April 2, 2007 1:08:25 PM

Wesner Moise writes: By using higher order functions, a function can "return" a value to an arbitrary location, and not just the caller, pretty much invalidating one of the claims of this post.

Vorlath Monday, April 2, 2007 1:34:31 PM

"The fact that purely functional languages are unsuitable for writing real applications will come as a great shock to the people using them for such purposes" - Anonymous #1

A bigger shock would be to find a computer that only uses pure functional applications and programming. It's like finding a magnet with only one pole. Oh wait, physicist believe in those too. Just like a single pole on a magnet, functional programming cannot exist by itself. You can build part of a system, but never the entire thing with pure functional languages.

---

Anonymous #2: It's not my example. It's the one in the paper. I'm the one who qualified it as simple though. Yeah, it works but it has a few assumptions.

---

Wesner Moise: By moving the function, sure. That's like saying you can come out in a different location than when entering a portable toilet if someone puts it on a train while you're in it.

Unregistered user Monday, April 2, 2007 5:17:06 PM

Isaac Carroll writes: > c := 0 > for i := 1 step 1 until n do > c := c + a[i] * b[i] > > Call me crazy, but that's as simple as it gets. You're crazy. :) c = sum (zipWith (*) a b) Makes the purpose of the code much more clear. > A bigger shock would be to find a computer that only uses pure > functional applications and programming. It's like finding a magnet > with only one pole. Oh wait, physicist believe in those too. Since you don't understand physics, you probably shouldn't talk about it. http://en.wikipedia.org/wiki/Magnetic_monopole > Just like a single pole on a magnet, functional programming cannot > exist by itself. You can build part of a system, but never the entire > thing with pure functional languages. Since you don't understand functional programming, you probably shouldn't talk about it. http://programatica.cs.pdx.edu/House/ TTFN

Unregistered user Monday, April 2, 2007 9:42:34 PM

Anonymous writes: *tsk* You accuse Backus of handwaving yet you perpetrate the offense yourself. Who says a function actually has to yield a result to its sender? There are plenty of useful functions that never return yet the process of reducing these functions does carry out interesting computations. Functional programming is less concerned with function evaluation than in the use of the function as an abstraction for describing a calculus of symbolic manipulations. Reading this post was a waste of my time.

Vorlath Tuesday, April 3, 2007 9:08:51 AM

Isaac Carroll:

That example is yours, not the one in the paper.

I do understand physics. In the wikipedia link that you yourself provided and obviously didn't bother to read, it said:

"a magnetic monopole is a hypothetical particle"

and if we look up hypothetical from dictionary.com:

"based primarily on surmise rather than adequate evidence;"

At the beginning of the wikipedia article, it also strangely says this:

"Interest in the concept stems from particle theories, notably Grand Unified Theories and superstring theories, that predict either the existence or the possibility of magnetic monopoles."

So we have hypothetical magnetic monopoles with a prediction that they do exist. Now compare with what I said:

"It's like finding a magnet with only one pole. Oh wait, physicist believe in those too."

You're way too nice going all this way just to show how I am, as always, absolutely correct.


"Since you don't understand functional programming, you probably shouldn't talk about it." - Isaac Carroll

So you're telling me this system you linked has no side-effects at all eh? No I/O? How does your functional software get its input? And I'm the one who's crazy?

---

"You accuse Backus of handwaving yet you perpetrate the offense yourself." - Anonymous

Oh good. I hope to learn something. I'm always looking to see where my mistakes are, if any.

"There are plenty of useful functions that never return yet the process of reducing these functions does carry out interesting computations."

If you have a box and you never open it, what's the point?

"Functional programming is less concerned with function evaluation than in the use of the function as an abstraction for describing a calculus of symbolic manipulations."

What does this eat in the winter time? Speaking of handwaving, what good are descriptions if they don't do anything? People need software that works, not a bunch of vague words put together to try and sound fancy. All you've said is that functional programming (who? you? your compiler? the technique? what?) is more concerned with ways to state math than it is with actually doing anything useful. Good luck with that.

"Reading this post was a waste of my time."

I thought you said you weren't interested in useful evaluations anyways.

Unregistered user Tuesday, April 3, 2007 1:26:45 PM

nonymous writes: For an exploration of what can be done with applicative programming alone, see the first two chapters of Structure and Interpretation of Computer Programs. The third chapter is a comparison of stateful vs. functional programming techniques. If you are looking for something more than "a bunch of vague words put together to try and sound fancy," I suggest you consult the available literature, rather than soliciting more extemporaneous blog commentary.

Vorlath Tuesday, April 3, 2007 5:40:06 PM

Section 3 only tries to coerce the definition of streams so that it fits more neatly into the functional paradigm when there's no need to do so. The author dances around the issue, but never attacks it directly which is a shame because many of the statements said are true. Having said that, being true does not make these statements paint the entire picture. Why can't streams be just that? Why do they need to be coerced and type cast into an infinite list? This would be a FAR more interesting subject because it would give acknowledgement to everything I'm saying. BTW, I'm already familiar with the link you provide.

FYI, the diagrams in that same section are flawed. See this article as to why.

Write a comment

New comments have been disabled for this post.

June 2012
S M T W T F S
May 2012July 2012
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30