I'm a mathematician. Math shares a lot in common with programming. So much that it's easy to conflate the two. This caused me a lot of misery.
The problem is that the constraints around the development of mathematics are entirely different than software. Math is abstract, and there's no tolerance for incorrectness. A single inconsistency and we throw out the entire thing. Math is also not stateful, it costs nothing to throw out a theorem.
Software is constrained in all directions: time, cost, performance, memory pressure, hardware compatibility, etc. For all that math shares with programming, practicing math shares very little with practicing programming. It was frustrating trying to design correct programs.
My misery ended when I started thinking about software as biological systems. Software isn't constrained by correctness, it's constrained by fitness. Software decays if it's not maintained. It needs a metabolism: it must capture free energy from the environment and use that energy to increase its fitness (eg, making money in commercial software, or being useful to its maintainers in free software). It must reproduce, "infecting" new hosts. Software grows through accreted complexity and mutation, not up-front design. Software is unfit for survival outside of its ecosystem; as the ecosystem changes, it must adapt. Software has competition, predators, prey, symbiotes, and viruses.
Another way software is similar to biology and physical systems is leaky abstraction.
Once you define something in mathematics, how you defined it does not matter. You can forget all irrelevant details.
In the software irrelevant minute details make systems incompatible.
As an example, Norbert Wiener defined ordered pair as:
(a,b) := {{{a}, Ø},{{b}}}
Hausdorff defined it as:
(a,b) := {{a,1},{b,2}}, where 1 and 2 are two distinct objects different from a and b
Kuratowski's definition
(a,b) := {{a}, {a,b}}
.. and there are many other definitions and variations, but still we have only one mathematics not many incompatible ones. Some mathematicians restrict them to subsets, but they don't create incompatible mathematics.
If you do something similar in programming languages, you get two different structures you can't use interchangeably without transformation (it incompatibility starts from little endian vs big endian and ending ends with vi vs emacs).
That is because mathematics tolerates informal proofs. Try to do formal mathematics (Coq, Agda, Isabelle/HOL, etc) and you will immediately see that it is not the case that {{a,1},{b,2}} = {{a},{a,b}}. Different definitions of "the same thing" are not "equal" but "isomorphic", and the isomorphisms are a bureaucracy that cannot be ignored -- unless you are doing regular, that is informal, mathematics and all this isomorphism business is just too tedious and 'obvious'.
The problem with programming is that it is always fully formal. It may be lacking in rigour, but it is formal nevertheless. A compiler cannot process informal code (syntactically or semantically) and there is nothing it can classify as "obvious". Hence you need to spell everything out.
Mathematics is like programming in pseudocode, that's why it's so neat.
> you will immediately see that it is not the case that {{a,1},{b,2}} = {{a},{a,b}}.
This is true for the examples you gave i.e. Agda and Coq but this is not a theoretical restriction. As long as you can formalize equality with something like axiom of univalence, you can prove similar things (e.g. forall x, f(x) = g(x) => f=g which gives interesting applications like mergeSort = quickSort). Proving univalence constructively is pretty hard, but it's at least partially solved, e.g. cubic type theory can constructively prove univalence. Agda has a branch that has cubic type theory implemented, if you want to check it out. There is also an "official" implementation of cubic type theory by the team who wrote the original paper but it's a pretty lean programming language without even unification.
It certainly feels like mathematics is better about leaky abstractions. It gets a lot more slack though because it's interpreted by mathematicians which can plug the leaks.
For example, there's multiple formal definitions of integrals. They aren't strictly equivalent! But when we calculate an integral, we don't concern ourselves with the differences unless they are relevant.
This distinction seems false. You internally apply a compatibility layer, transforming for interchangeability and caching your computed equivalences. The same is frequently done in software.
I generally agree, though I have some issues with parts of the biological analogy. I believe there are two different meanings of "software" conflated here.
If we look at software as a social phenomenon, then all the constraints you mention apply. However, if we look at software just as an executable artifact - it does not decay. Bit rot isn't real. Hardware can break down, but bits don't magically flip out of order just because the original developer went AWOL. This is important, because there seems to be this notion in our industry that everything needs to be updated and updatable all the time. But the truth is, unless someone purposefully screw it up (time-limited software, or Internet dependency), I can boot up my old machine and still do real work with decade+ old software.
Some types of software are much more resistant to rot than others. If your program has a URL, IP address, hostname, or file path embedded in it, those will eventually fail. If your program is compiled for (or designed for) a CPU architecture that isn't produced anymore, it's effectively rotting (even if you can run it in a VM, it's become second-class). If your program depends on OS functionality (practically all programs) that changes, it rots. If your program is designed to communicate with other processes, those processes will eventually become unavailable.
One of my favorite pieces of software is the window manager Xmonad. It's written in Haskell and is very reliable. I can't recall ever experiencing an error. Yet regrettably, I'm going to have to find a replacement eventually because X11 is being obsoleted by Wayland. It's only useful because of its dependency on X11, which in turn is only useful because of the ecosystem of software around it.
Speaking of decade old software, X11 is well over a decade old and doing well. It's evolved a good bit since, but is largely the same. In comparison though, the Pythagorean theorem was proven by the Greeks over 2000 years ago, and by the Chinese even further back, and is just as valid now as it was then. I get somewhat pensive when I consider that none of the software I've written will last remotely that long.
The analogy is a poor one though. This is nothing like rotting. A rotting thing changes, whereas here software stays the same but the environment around it changes. I know, no analogy is perfect, but this analogy is downright misleading.
I'm trying to think of a better analogy. At first I thought of stagnation, like how a river that gets blocked and can't flow gets filled up with algae and gunk.
But then what you said about software staying the same while the environment changes around it immediately made me think of evolution, Like any species that can't keep up with changes in it's environment, software that is not continuously updated to fit it's.
IMO again, this apply only along the social dimension of software. I can still boot up my decade-old computer and use decade-old software to do useful work (hell, many if not most factories do that all the time). I can be screwed over if I go on-line - either by getting pwnd, or by letting the OS update itself - but as long as I stay away from the Internet, the machine will keep working, and so will the software.
(Then again, 'smilliken turned out to be talking about much larger timescales - not decades, but centuries. There, no software artifact will survive, only its mathematical essence.)
Computing as biology is the (temporary) perspective Alan Kay states in his famous quote: "We don't have a science of software engineering. Until we do, we should take advantage of late binding" [1]
Once we are able to figure out things like model checking, program analysis, abstract interpretation and type systems, we will be able to do computing in a similar way to that of Mathematics. Or at least a bit closer, I think.
This is a really bad argument imho. I'm an agda programmer, but only for hobby projects. You can do pretty much everything you listed: "model checking, program analysis, abstract interpretation and type systems" in agda, right now, this very day and some of these stuff is built-in (like dependent type system) or in the standard library. There even is a small industry built around proof automation. But it doesn't follow agda will solve software engineering. When it comes to engineering, it's also about constructing, not just about correctness. Everything you listed solves the latter while ignoring the former. I think the fundamental problem with software engineering is that it is a little too much like mathematics, to the point, the only way to be correct is to be as precise as agda, but, people who practice software engineering, again, just like mathematicians don't care to use this precise language. Just like a mathematician will never write his paper in agda instead of human language, unification will never by itself solve software engineering. Programs are written for humans, not for automated proof checkers, just like papers on mathematics are written for humans.
"Math shares a lot in common with programming. So much that it's easy to conflate the two."
Well, I would like to point that both mathematics and computer science are human activities which occur on a substrate of determinant computational processes. Mathematics has a culture, history and set of standards which decide which among results of "proof processes" are "mathematically interesting" whereas Computer Science and Software Engineering engineering also have similar standards determining what's significant among determinant processes.
Naturally, the two standards are distinct though actual results may occasionally intersect. Moreover, being "mathematically interesting" may make be a given item useful for computer programmers at times. Comprehensibility is important for our theories about programming despite programs having the usual requirement (unlike mathematical proofs) that they need a concrete manifestation.
And both mathematics-the-human-endeavor and software-engineering-the-human-endeavor are full of metaphors that are false-but-useful. Computer programs don't literally decacy any more than mathematical truths have "inherent beauty" or exist in any ideal realm. But both metaphors are useful nonetheless.
Also, the parent is confusing logical levels - programming is analogous to mathematical calculation whereas mathematics is analogous to computer-science or software engineering. Programs may be incorrect and calculations may be incorrect so many of distinctions are not germane to math/computer-science split (though the different ways programs are incorrect versus the ways calculations can be incorrect are relevant).
Please correct me if I've misunderstood Curry–Howard, but the way I see it: In mathematics we can take a proposition, and then try to find a proof for it. Depending on the proposition, this can be extremely challenging. And in programming we can write the type of a function, and then try to write some implementation that our type checker will accept. If the function type is complicated enough, then finding _any_ program that has that type can be challenging. And Curry–Howard correspondence shows us that there's a deep connection between trying to find a proof for a proposition and trying to find any program that has a given type.
But when I program, then "trying to find any program that has a given type" rarely feels like the main problem I'm trying to solve. Depending on the program, I may want the program to do any of these things: run efficiently, conserve memory, have a good-looking and intuitive user interface, support multiple languages, be secure, be maintainable. If it's a web app, I want it to support multiple browsers. If it's a game, I want the challenge level to be just right. If it's a software instrument, I want it to sound good. You get the idea. So yes, there exists an isomorphism between programs and proofs, but I'm not really sure what to do with it, since the isomorphism doesn't preserve most of the properties I care about.
Dependently typed programming languages seem capable of defining types that express many aspects of a program’s specification.
Formal specification in that sense isn’t enough to guarantee that your game is challenging—but it’s an engineering discipline that helps make sure your implementation is correct, and that you have clear and well-defined intentions.
Curry-Howard and dependent types is far from the only way to use logic for reasoning about programs!
One cool thing I’ve seen is using linear logic to make prototypes of games. Linear logic is good for expressing rules that consume and produce resources, in a way that fits very well with many game rulesets. Thinking of games as logical systems means you can ask questions like “is level 3 possible to finish starting with the items found in level 2?” (A proof could be a sequence of actions that start with those items and eventually reach the level’s end state.)
Logical reasoning isn’t the only aspect of software development, just like structural engineering isn’t the only aspect of building houses.
Homotopy Type theory also shows some deep connections between mathematics and programs, as the progress of the theory has bee characterised by building programmatic proofs _first_, to generate new mathematics. That's an inversion of the usual process.
Doing mathematics is really nothing like software programming. Yes, there is an isomorphism between proofs and programs, but that doesn't mean much for the vast world of mathematics outside logic
There are isomorphisms between any place of that vast world of mathematics and logic too.
Math is also constrained by fitness, not perfection. Just take a look on the number of unproved conjectures we keep around. It's just that the Math community generally places more value on proofs than the programming, but it is a continuous, and there are more tolerant subcomunities of mathematicians and very intolerant ones of programmers too.
I didn't mean that mathematicians don't write code; I meant that doing most mathematics isn't anything like doing programming, even when you're programming in Haskell. Just because two things are isomorphic doesn't mean they're used in the same way. For example, programming is formal but rarely rigorous; mathematics is the opposite.
Most mathematicians do work that is very different from most programmers. Some programmers do work that is similar to most mathematicians, and some mathematicians do work that is similar to most programmers.
Programming can be very rigorous, and mathematics can be very formal and not rigorous. There is a huge middle ground, sparsely populated between those.
Mathematics isn’t any less messy than software development - it precisely is software development. A theorem is a program we run on our wetware, and if a given wetware runtime doesn’t support the memory or time requirements for a theorem, it won’t run correctly.
It’s also no less messy than anything else. Mathematics can still only offer us relative truth, and the differences between a theorem and a sharp stick are pretty superficial.
> A theorem is a program we run on our wetware, and if a given wetware runtime doesn’t support the memory or time requirements for a theorem, it won’t run correctly.
You can use a theorem without "running" it, though. The classification of finite simple groups took thousands of pages to prove; it would be pretty inconvenient if you had to read all those pages to use the theorem!
You're right. Mathematics is a program that runs in a brain, which is a wet computer.
Subjectively, the distinction is useful. I'm never going to have a different brain, and I won't outlive my brain, so for all intents and purposes, my brain is an eternal constant. Theorems that ran on my brain once have accomplished their goal of convincing and never need to be run again. They are immutable.
This article is interesting in terms of producing useful though / mental discussion, yet on completing that thought I disagree with almost all of it.
- Programs are not like cars. Programs are like cars. They are a manufactured construct created for a specific purpose, which does have relative degrees of correctness.
If I have a car with no wheels, it is not a "correct" car, it is at best a chair. No engine or drivetrain, similiar - not "correct" car. Oddly enough, no seat, possibly still "correct". Hiccuping or jumping behavior, it has a bug, or a glitch.
- Software should be pure logic automata. In many respects I would argue this can be a "bad". Such software can be extremely limited - it does X, no more. Or, it can be very brittle. It does does X1, but if I try X2, it breaks. Software like biology is in many ways better.
I feed my software an apple, it eats it, producing work and waste. I feed my software a steak, similiar. I hand my software a baseball, it throws it. I hand it a football, it does similiar. I hand it an apple, it may eat it or throw it depending on the need.
- Faith healing. This has been a meme since nearly the dawn of computers. "Have you tried restarting it?" Maybe kicking it. Wifi won't work? Bad spirits in your house. I worked with fluid mechanics for years, and many (non-trivial) simulations seemed about as likely to complete due to system noise as they did due to program correctness.
Further, there is a serious ivory tower attitude here towards magical thinking and superstition. Technology shamanism is a very real thing. People gravitate towards it often because it works. Its oddly scientific method. If I have my lucky rabbit foot, the program compiles. If I don't, system crash.
Biological systems are logical too. The genome is the program that is hardwired into them. They also run on mathematics involved in chemical reactions. Even the simplest organism like the single-celled amoeba has more complicated and highly optimized algorithms programmed into its genome for survival than the most complex system (like the LHC) ever built by humans. An amoeba behaves the way it behaves because the laws of physics imbued in the mathematics of its chemical reactions caused it to overwrite its own gene code in such a way so as to optimize the mathematical probability of its own survival. Mathematics is merely an abstraction of our mind to understand the reality of our world. The programs that we create solve problems in our world, but are constrained by the same kind of variables that constrain biological systems, but both biological and software systems could be interpreted in terms of mathematics.
No real world biological or physical system is 'simple'. Every program starts simple, but even that is up to interpretation. A one liner program in any language is extremely complicated once you factor in the OS, the compiler and the interpreter that will actually run this program. Once the program intakes more complicated functionalities, and also multiple programmers, it becomes anything but simple. Simplicity is good to organize our thoughts and design systems, but it a whole different matter to expect that the designed system will also stay simple. Hence just like any other system in nature, software also has to evolve, and there shouldn't be anything wrong with that. A mathematical proof is not a panacea for the problems in software development. The purpose of software changes, its constraints change, and proofs will have to be rewritten everytime. You don't expect the underlying laws of mathematics to change, that is why once proved, a theorem stays proved. But in software it is expected that the system's underlying purpose will change with time. So proving the current state is what we can do, which is not of much consequence as time goes by.
It's an interesting article but I feel like it fails to actually illuminate what's behind the distinction between logic and biology.
"Biology is very different from logic—or even from the physical sciences. If biologists find that a particular mutation of a gene is present in 80% of people suffering from a certain disease, and missing from 80% of a control
group, then that’s a significant result. If physicists tried to report an experiment in which 80% of the events supported their theory, they’d be required to explain what happened in the other 20%"
This may be true but accuracy by itself is not a very "deep" distinction. If you have crude engineering, you can accept a low percentage of accuracy. If you happen to have done good bioengineering, 80% might not be good enough accuracy.
A much more illuminating quality of biological systems is A. they are much more complex and B. they are tolerant of variation everywhere, that biological systems with varying properties interoperate without major problems whereas computer components require very fine tolerances to operate together. Human organs vary from person to person yet different people remain alive. If a given computer component varies substantially, chances are the machine won't stop.
The variation of each component of a biological system relates to another property of biological systems - "self-organization"; each of the components of a biological system appears through a million-year long process of self-reproduction. So each component has a bit of something (feedback, intelligence, design, etc) which allows it to keep going even when inputs and outputs are imperfect.
And finally, there's the quality of lack of any overall idea. Genetic code involves a haphazard of grouping of "lasagna code" that merely happens to work, that X component accomplishes N tasks, M of which serve no current purpose that's OK. These M purposes functionalities may have served a purpose in the past and may serve a purpose in the future but maybe neither. The methodologies operating haphazardly by itself serves the purpose of having a variety of instances of an organism that can adapt when the environment changes.
"An automobile is a piece of machinery, a program is some kind of mathematical expression"
If I build a mechanical computer to output the digits of PI is that a piece of machinery, a program or a mathematical expression?
A computer is a piace of machinery capable of running programs. You're building a specialized hardware to run a program - so I guess it's still a piece of machinery even if capable of running one program.
Hmm, while I wouldn't argue that program = automobile, I find the author's arguments unconvincing, especially the part about meaning. Both car and software are the tools and in case of every (well designed) tool it's easy to provide the answer to author's question: "what is it supposed to do?". For example, car is supposed to move people and goods quickly and safely. And I think proving that "meaning" wouldn't be harder that proving the meaning of any sufficently complicated software. For example MS Word :)
Perhaps Prof. Lamport is giving too much credit our understanding of automobiles this time around:
"There are no homeopathic automobile repair
shops, that try to repair your car by putting infinitesimal dilutions of rust
in the gas tank. There are no automotive faith healers, who lay their hands
on the hood and pray. People reserve such superstitions for things that they
don’t understand very well, such as the human body."
I don't think that is true. I routinely see automobiles that are 'blessed' on the streets of San Jose (quite likely the owners of these vehicles are developing software that this paper talks about).
True. This can be seen everywhere, from food to football. I believe it can be boiled down to two things: uncertainty, which makes some people desire reassurance from supernatural, and non-obvious failure modes, which makes selling this reassurance pretty much risk-free.
Yes, most of us not do not seem to be able to resist the desire for the reassurance as you put it.
My reading of the paper is that it is more about the lack of mathematical (perhaps rational or logical may be the right word) approach to program development than proving programs or lack of proofs.
Woo is in market competition with evidence-based solutions. Mechanics fix your car essentially every time, in exchange for money. Doctors fix you sometimes, in exchange for a lot more money. So obviously the woo is going to thrive a lot more in the second market.
I prayed for the starter motor to be able turn the crank with whatever charge was left after my brother borrowed my car and drained the battery by leaving the headlights on.
This is very fluffy for Lamport, nothing of real substance in this essay.
I did get a chuckle out of this:
> In retrospect, thinking of programs as automobiles wasn’t so bad. Automobiles are pretty simple. If their car stops working, people expect any good mechanic to be able to figure out why and fix the problem.
Ok, I guess a century of engineering has produced some pretty simple machines :) /s
Hrmn. The various analogies are perhaps a bit fluffy, and the email and drag-and-drop examples are perhaps not particularly convincing.
But man, I wish Lamport's call for our industry to think more logically and design programs more mathematically would gain more traction.
These little inconsistencies add up to create horrible messes. This is true everywhere from our programs to our languages to the core abstractions and APIs that we work with. Some examples off the top of my head:
If we were great at engineering, a century's worth would have ideally led to simpler machines. Simplicity = sophistication.
(Before you hate on me, please know that I mean things should be as simple as possible, but no simpler. Cars, I believe, are not as simple as possible. They are incredibly complex. That said, look at a Tesla. Mechanically speaking, they're impressively simple machines.)
And yet I have a friend with a Triumph motorcycle who has brought it to multiple mechanics and there is an electrical issue somewhere that nobody has been able to definitively fix. Maybe not so simple even after a century of engineering so many variations on a theme.
The problem is that the constraints around the development of mathematics are entirely different than software. Math is abstract, and there's no tolerance for incorrectness. A single inconsistency and we throw out the entire thing. Math is also not stateful, it costs nothing to throw out a theorem.
Software is constrained in all directions: time, cost, performance, memory pressure, hardware compatibility, etc. For all that math shares with programming, practicing math shares very little with practicing programming. It was frustrating trying to design correct programs.
My misery ended when I started thinking about software as biological systems. Software isn't constrained by correctness, it's constrained by fitness. Software decays if it's not maintained. It needs a metabolism: it must capture free energy from the environment and use that energy to increase its fitness (eg, making money in commercial software, or being useful to its maintainers in free software). It must reproduce, "infecting" new hosts. Software grows through accreted complexity and mutation, not up-front design. Software is unfit for survival outside of its ecosystem; as the ecosystem changes, it must adapt. Software has competition, predators, prey, symbiotes, and viruses.