
The Power of Ten – Rules for Developing Safety Critical Code - kiyanwang
http://spinroot.com/gerard/pdf/P10.pdf
======
vog
This is an all-time classic.

Too bad that the article doesn't mention the original paper:

[http://pixelscommander.com/wp-
content/uploads/2014/12/P10.pd...](http://pixelscommander.com/wp-
content/uploads/2014/12/P10.pdf)

Some interesting HN discussions around applying these NASA coding standards to
JavaScript:

[https://news.ycombinator.com/item?id=8856226](https://news.ycombinator.com/item?id=8856226)

------
ranko
The original paper describing and justifying these rules in more detail is at
[http://spinroot.com/gerard/pdf/P10.pdf](http://spinroot.com/gerard/pdf/P10.pdf),
and the official document is at [http://lars-
lab.jpl.nasa.gov/JPL_Coding_Standard_C.pdf](http://lars-
lab.jpl.nasa.gov/JPL_Coding_Standard_C.pdf)

~~~
sctb
Thanks! We updated the link from [http://www.rankred.com/nasa-coding-
rules](http://www.rankred.com/nasa-coding-rules).

~~~
guntars
I understand the need for linking to the original source, but I just looked at
both and the latter is arguably harder to read.

~~~
teh_klev
Yes, but the rankred article hasn't reproduced the full paper, it's just
cherry picked and summarised in a way that you would have no idea if you
missed something worthwhile.

The original paper isn't that terrible to read.

Also the amount of third party cookie crap that site tries to deposit in my
browser is crazy.

That's why HN prefers source material, not blogspam stuff like this which adds
nothing new and often removes possibly useful information.

------
garethrees
It looks to me as though the recommendation that RankRed has titled "Rule No.
5 — Low Assertion Density" would be better described as "High Assertion
Density" — the recommendation is for a _minimum_ of two assertions per
function (and functions are supposed to be short per rule 4).

The recommendations look good to me and (with one caveat) correspond to rules
that I apply when writing C code with a high reliability requirement.

My one caveat is in "Rule No. 8 – Limited Use of Preprocessor" which bans all
complex uses of the preprocessor. The problem is that it is common in C to
encounter situations where the only way to avoid lots of code duplication is
to store a table of facts in a macro definition and use the preprocessor to
expand those facts into the relevant bits of code in each place where they are
used. So in these situations you face a trade-off between the risks due to
complex preprocessor use, and the risks due to code duplication (namely, a
maintainer might change a fact in one place where it is used but fail to
change it in another). My experience is that the risks due to code duplication
are very high, and so it's worth the risk of using complex preprocessor macros
to avoid them. The risks can be mitigated by implementing the necessary macros
in a structured way to keep the complexity under control:
[http://garethrees.org/2007/04/24/relational-
macros/](http://garethrees.org/2007/04/24/relational-macros/)

~~~
titzer
Assertions in code cut both ways. Sometimes they can be great; telling you
exactly which assumed invariant is violated. However, that doesn't tell you
where or how the invariant was violated.

Sometimes assertions are just crutches for lazy programming. Instead of
handling of a very valid (corner) case, some people just assert that it
doesn't happen. Lo and behold, years later, it does happen. And those years
later, the context is completely lost. How easy is it to handle the corner
case now? Hard. In this situation, instead of simply asserting that it doesn't
happen, it might be warranted to just assume that it _can_ happen and handle
the case. Then, followed up with coverage testing, one needs to (actually try
hard) come up with an input that triggers it.

Assertions can also have bugs. The worst is trying to figure out which
assertions are really valuable and which aren't.

Shotgun-spraying assertions in the code is not a good strategy in general.

~~~
garethrees
Obviously a recommendation to have a high assertion density does not mean
"shotgun-spraying assertions in the code".

If your point is just that the rule could be applied mechanically and without
thinking and that would be bad, then that's true, but it applies to
everything, not just to the rule about assertions. Someone could apply the
"keep functions definitions below 60 lines" rule in a perverse way, by
splitting every long function definition at an arbitrary point in the first 60
lines and tail-calling a continuation. It doesn't mean the rule isn't a good
one.

~~~
titzer
In my experience that what's will happen in practice, especially when there is
a specific metric attached. Doubly so once there is a mechanically enforced
required amount of assertions.

Wouldn't it be better if we could create programs that were correct by
construction (and thus needed no assertions)?

~~~
garethrees
I see — your experience suggests that rules inevitably get turned into
thoughtlessly evaluated metrics that then get gamed.

It is a shame when that happens, but when it does, it's not the fault of the
rules, it's the fault of the organizational culture. The rules would still be
valuable if you used them thoughtfully and with the aim of improving the
reliability of the product, not gaming some management system.

~~~
efaref
I don't think they're intentionally gamed, it's just part of human nature. If
you don't have to defend your assertions (because more assertions is presumed
better, as per the rules), then people are liable to err on the side of
putting more in that they need to.

With the exception of assert(ptr != NULL) assertions, I think most of the ones
I've hit have actually been completely duff, and with some thought could just
be removed. I dread to think what would happen if I grepped the commit
histories of all the projects I've worked on for "removed duff assert".

------
thearn4
Speaking from experience, the biggest problem with NASA's software engineering
requirements
([http://nodis3.gsfc.nasa.gov/displayDir.cfm?t=NPR&c=7150&s=2B](http://nodis3.gsfc.nasa.gov/displayDir.cfm?t=NPR&c=7150&s=2B))
is the way that they tend to feed down into non-safety critical projects. It's
getting better though.

~~~
pjmlp
Sadly quality is highly disregarded in our field.

I dream of the day when not making use of contracts, static analysis and type
based programming is seen as quality smell and not something that only a few
are allowed to make use of.

~~~
lambdadmitry
It's not "disregarded", it's eclipsed by a need to ship often and ship a lot.
There is a tendency to overstate amount of problems that come from bugs
because they are painful to debug. Therefore, somewhat experienced programmers
are often too defensive and suspect to paralysis by analysis (of which uber-
complicated and rigid typing is a sort). Even if some advanced typing system
will save a few days of debugging after refactoring down the line, it doesn't
matter if a competitor ships buggy RoR-based systems a few months earlier.

Advanced typing/static analysis is not a pinnacle of software engineering,
it's a set of training wheels. Useful? Yeah. But sometimes you may need to
skip them.

~~~
mk89
> ... it doesn't matter if a competitor ships buggy RoR-based systems a few
> months earlier.

You hit the nail on the head! However, I would like to mention that
competition is _only_ one very important factor.

I'd add that IT is a cost as it is a baker for a bakery, a cook for a
restaurant, a mechanic for a repair shop. Do you want to be the n.1
restaurant? Then you must pay, so that your business is _good food_ , not
marketing - notice: the best restaurants don't even have good websites, as the
food speaks for them. Yes, you must pay a lot to get the best cook, who will
take all the time and require all the staff he wants/needs to prepare the best
recipes, and so forth. We think we are in a better business just because IT is
something relatively new, which not everyone fully understands, so we think
that we are the best in everything we do, because we bring innovation. Sadly,
we are _not_.

The _current_ reality is that most companies are average or below-average with
no great plans to further develop or to become the next IBM, NASA, Intel, or
whatever. Most of them want to survive, some want to get a lot of money fast,
and who knows, maybe in 5-10 years sell the company for 1,2,10 mln USD. As
long as the mentality is driven by the idea of the acquisition, not by
ambition, things won't change - the most efforts will be spent in hiding the
pile of s __* that employees do, trying to draw the right numbers on the right
graphs. And even IBM, Nasa, Intel fail. They do. The difference lies in the
ability to stand up and get on their feet again - learn from failures. They
have also _wasted_ huge amounts of money - it's documented everywhere, every
time one of their projects gets shut down. However, they have a goal in their
mind: to be the n.1 whatever they do.

~~~
akavi
> Do you want to be the n.1 restaurant?

Depends on what you mean by "n.1". The "n.1" restaurant in terms of monetary
value is McDonalds. They do market (heavily). They don't pay for the best cook
(not anywhere near). And they don't have the best recipes. But they are
_wildly_ successful. Conversely, michelin star restaurants go bankrupt all the
time.

There's nothing wrong with aspiring to produce michelin star code. But as
McDonalds shows, you can be incredibly ambitious and long-lasting with quality
being at best a secondary priority.

~~~
dragonwriter
> The "n.1" restaurant in terms of monetary value is McDonalds.

No, the No. 1 franchised chain of restaurants is McDonald's; there's a pretty
big difference between the No. 1 _restaurant_ and the No. 1 _chain of
restaurants_.

> But they are wildly successful.

The chain has, over its lifetime, been wildly successful, a success initially
fueled by a reputation for _extremely high quality_ , consistency, and
efficient service compared to the greasy-spoon diners that were the mainstay
of low-cost restaurant food at the time of their initial growth; the recent
history is something a bit different (they have, IIRC, recently ended a period
of year-over-year drops in same-restaurant sales by, among other things,
significantly cutting the number of restaurants, particularly the corporate-
owned rather than franchised ones.)

> Conversely, michelin star restaurants go bankrupt all the time.

So do individual (independently owned and operated) McDonald's restaurants.

> But the goal of most companies isn't critical accolades, it's to make money.

Yeah, but McDonald's didn't make more money than other restaurants by skimping
on quality, they made more money by being successful based on quality, and
moving quickly with that success to branch out to other locations rather than
merely saturated a single market, and by offloading much of the risk (and some
of the rewards) of expansion to fee-paying franchisees, which is what
differentiates them from single-location restaurants that are trying to
extract maximum value from a particular local market.

Trying to generalize from this to say anything about succeeding in the
software market is probably pointless, mostly resulting in very bad analogies.

~~~
kbenson
McDonals is the Ford of restaurants. Automation (using procedures and
equipment) to reduce cost and keep a consistent relatively high level of
quality.

------
Symmetry
Regarding Rule 4 - No Large Function - I think John Carmack had a pretty good
explanation on why that's not really a good thing:

[http://number-
none.com/blow/john_carmack_on_inlined_code.htm...](http://number-
none.com/blow/john_carmack_on_inlined_code.html)

~~~
GregBuchholz
>The fly-by-wire flight software for the Saab Gripen (a lightweight

>fighter) went a step further. It disallowed both subroutine calls and

>backward branches, except for the one at the bottom of the main loop.

>Control flow went forward only. Sometimes one piece of code had to leave

>a note for a later piece telling it what to do, but this worked out well

>for testing: all data was allocated statically, and monitoring those

>variables gave a clear picture of most everything the software was doing.

>The software did only the bare essentials, and of course, they were

>serious about thorough ground testing.

>No bug has ever been found in the "released for flight" versions of that
code.

I was pretty excited to learn more about that style, and then I came across...

[https://www.flightglobal.com/FlightPDFArchive/1989/1989%20-%...](https://www.flightglobal.com/FlightPDFArchive/1989/1989%20-%200734.PDF)

...but I'd still like to know more about this style of programming, and
languages that facilitate programming in that manner.

~~~
dragonwriter
If I read that article right, it sounds like the software fault attributed to
the crash is that the "control laws" that the software implemented were
improperly chosen, not that the software did not function as intended.

Which is to say, a serious problem, but if we take "bug" to mean a difference
between designed function and realized function, not a "bug", and therefore
not something which should tarnish the record of the _coding style_ to which
the bug-free nature of the Grippen software is attributed. The problem seems
to have been at a different level than coding.

~~~
safeignorance
You're correct to point out that the coding style isn't to blame for the
software fault. And IMO The last paragraph of the article hints at the most
probable fundamental cause.

But I just don't buy that this was not a software fault. It clearly was a case
of a faulty software specification.

 _> The problem seems to have been at a different level than coding._

This makes me queasy. Software engineers working on these sorts of systems --
-- or at least a few senior ones -- should understand enough of the domain to
say "this spec is not adequate" or "bad things might happen under conditions
xyz; what's the correct behavior in these cases?". And of course all software
should notice and then degrade gracefully when assumptions are observably
violated.

To absolve the software engineer of any responsibility for understanding the
context surrounding his software is to wrongly assume there's not much to
software engineering beyond programming to someone else's spec.

~~~
dragonwriter
I absolutely see it as a software problem and a software engineering problem,
just one orthogonal to considerations of the value of the coding style adopted
in preventing code that deviated from specifications.

------
atsaloli
I posted a write-up of Dr. Holzmann's talk "Mars Code" which he gave at USENIX
Hot Topics in System Dependability '12
([http://www.verticalsysadmin.com/making_robust_software/](http://www.verticalsysadmin.com/making_robust_software/)),
about how NASA/JPL writes and tests software that survives the rigors of
interplanetary travel; planetary entry, descent, and landing; and operation in
another world. :)

------
ballooney
This is a very interesting talk about writing the software for the Curiosity
Mars mission by the head of Software Reliability at JPL, Gerard Holzmann, who
also wrote these 10 rules:

[https://vimeo.com/84991949](https://vimeo.com/84991949)

~~~
Bromskloss
Nice tool setup. I would like to work under those circumstances.

------
petra
If we're talking about high reliability code, one thing claimed about Haskell,
is "if it compiles, it has no bugs".

How close is it to the truth ? And how close are we to having that kind of
capability for real-time programming(even assuming we're willing to forsake
protability, community, and maximum efficiency to some extent ) ?

~~~
kowdermeister
I doubt that statement can be true in any programming language. Bugs come in
many shapes and a functional language can prevent a fraction of it. You can
have logic errors, misunderstood requirements, wrong database queries etc, the
list is pretty much infinite.

~~~
sa1
The
[https://en.wikipedia.org/wiki/Curry–Howard_correspondence](https://en.wikipedia.org/wiki/Curry–Howard_correspondence)
says there is a correspondence between any logical statement and a type in a
sufficiently advanced type system. So yes, there are languages aimed at
eliminating logic errors, and Haskell goes pretty far(though its type system
isn't quite advanced enough).

~~~
kowdermeister
If I grasp that right, it says that if something is provable mathematically,
then you can write a program for it with an equal meaning.

I still don't see how that prevents user error. My question is then how do you
mathematically prove intent?

Also, how far should "sufficiently advanced" be?

We already have a tool for that in the forms of unit tests and types does help
if the subject is abstract enough.

~~~
oconnore
You can't prove intent.

However, you can consider the formal specification of your intent (the type),
to be an example of fully declarative programming.

Since you write your type without any care about how it might be executed --
the holy grail of abstraction -- you are less likely to make errors.

When using a type system that isn't capable of fully specifying what you're
doing (i.e. Haskell), you are of course subject to making implementation
errors within the range of possible programs that type check. But in practice
it's usually enough to catch the sorts of mistakes that you're likely to make.

~~~
pron
> However, you can consider the formal specification of your intent (the
> type), to be an example of fully declarative programming.

The vast majority of formal specification and verification tools (I believe
Coq and Agda are the only exceptions, and they are rarely used in the
industry) express the intent directly in logic rather than in the types (HOL
in Isabelle and HOL Light, ZFC+LTL in TLA+ and maybe Scade, ZFC in Alloy, and
a typed set theory in SPARK, I believe).

> But in practice it's usually enough to catch the sorts of mistakes that
> you're likely to make.

I think this claim is supported by little evidence. Most non-dependent type
systems are extremely weak (or require cumbersome encoding) to express even
all but the most trivial of properties (e.g. they can't even express that the
value returned from a max function is indeed maximal, let alone more elaborate
properties). Their expressive strength is that of a finite-state machine. How
much does that prevent real bugs requires empirical study.

------
lordnacho
How does that fixed upper bound on loops work? If there's an array of dynamic
size that needs to be looped over, how do you do that?

~~~
robmccoll
#define MAX_NUM_OBJS 100

for(int i = 0; i < num_objs && i < MAX_NUM_OBJS; i++)

Combined with the no dynamic allocation rule, you can guarantee that the
number of elements in the array must be less than some maximum since you have
limited dedicated space for storing them.

~~~
lordnacho
There might not be a valid object at every location in the preallocated
memory.

Say you have a list of visible satellites, and the number of them can change
as they go in/out of view. There's never more than 100, but sometimes there's
50 and sometimes 60.

I supposed you could have an object with an INVALID flag, which the loop can
use in its logic?

~~~
robmccoll
That's a good point. If you do have a situation where the number of objects is
somewhat dynamic, you could use layers of protection such as (re)initializing
the unused objects to a known safe state, adding a flag as you mentioned,
adding sentinel values to the end, and at the very least you do have the
guarantee that you aren't running off into memory that was actually dedicated
to another purpose and contains fundamentally different data. If you are doing
something really dynamic, you could embed two linked lists (or an embedded
list and a free index stack or even two packed stacks). The reason for stacks
over lists is that lists can accidentally become cycles.

------
hackits
Found the rule `There shall be no use of dynamic memory allocation after task
initialization.`

They must use pre-allocated string buffers for some of the task's. Though I
figured if you're doing a flight controller for a jet fighter or sate-light
you don't need a lot of string parsing.

~~~
TickleSteve
dynamic memory allocation is very much frowned upon in embedded systems, not
just for strings.

Everything should be static and deterministic at all times. This is the
easiest/only way to ensure you have no resource issues.

You should always (statically) allocate for maximum/worst case.. because you
_have_ analysed your worst-case, haven't you?

~~~
petra
How do you analyze worst case ? don't you need to know what calls what, up to
what depth, and that's dynamic by nature ?

~~~
Unklejoe
As said by TickleSteve, you define the worst case rather than analyze it.

As for the stack usage requirements, it seems like this could be determined
statically by some parametric process, but I’m no expert on this.

Does anyone see a reason why there couldn’t be some algorithm to statically
analyze some code to derive the worst case stack usage?

For example, take every function and assume that every variable declaration
will be required. Add them up. Then, follow every path down the call tree
while adding up the required stack for each call.

~~~
TickleSteve
...in a typical GCC based system, you can for example use "-fstack-usage" and
"-fcallgraph-info" to determine a worst case.

(tho that takes a bit of analysis, there are tools around that can automate
this).

------
DominikR
Recently there was a thread here where many argued against some company owner
that has set the objective that the code created by his employees should be as
simple as possible.

Often the arguments boiled down to: It looks only clever if the skill level of
your employees is too low, you should work on that.

When I look now at the rules that NASA enforces, it seems to me that their
objective is to prevent bugs by not allowing code that is overly complex and I
doubt it's because their engineers are not clever enough.

In fact I believe that it takes a lot of cleverness to create code that seems
so simple that you don't even need to read the documentation.

~~~
smaddox
It seems that people often confuse complex with clever or impressive.
Impressive is unifying the falling of an apple with the orbiting of the
planets into three simple universal laws.

Complexity is an emergent phenomenon. It occurs when there are too many
interactions to track. To make something simple is to take all of that
complexity and to understand and model it as the interaction of a few simple
rules repeated over and over and over.

~~~
DominikR
Great analogy, I agree!

------
fazkan
I think the compiler is as much responsible for secure code as the code
itself, tools should be updated regularly with these core concepts in
mind...These 10 points, I figure are just the starting points, there are a lot
of other places where the flow of program changed by abusing simple keywords
such as volatile. Which is where the correct use of compiler, serve as a
nightwatch to stop any undefined behavior from crossing the wall....

------
avindroth
My first two years at undergrad would have been so much easier if I had read
this piece of document.

I remember using a quadruple pointer (char ____) in one of my systems classes.

Also, after having dabbled in Haskell, I know the value of static analyzers.
That is like good practices on steroids. And you start to avoid these errors,
and start developing better practices.

Change the environment to change your habits.

------
atemerev
All of these make perfect sense, but I would have allowed tail recursion (and
only that). Its properties are well-defined and boundedness can be easily
proven (also it is easy to distinguish tail and non-tail recursive calls).

~~~
gpvos
They're using C. Many C compilers don't optimize tail recursion away, and even
for those that do, it is not generally obvious from the C syntax whether
something is a tail call or not.

------
finishingmove
> a function shouldn’t have more than 60 lines of code.

I started having headache spikes after reading this sentence that refers to
keeping functions "short", in the article about safety critical programs.

~~~
DasIch
Keep in mind that we're talking about C code here. Error handling etc. blows
that up quite significantly.

------
geophile
In rule 5 (about assertions), why the comparison to true? Why is "if (<boolean
expression> == true)" preferable to "if (<boolean expresson>)"?

~~~
Udo
I assume it's a habit to make sure you communicate to the type checker: "I
expect this expression to be boolean".

------
lisper
Rule 0: don't write safety-critical code in C.

~~~
AnimalMuppet
Nice snark, I guess, but... did you actually read the article? One of the
reasons for C is that the tooling around it gives you much more support. For
safety critical code, you need that, because programmers make mistakes, no
matter what the language.

So if you write safety critical code in Haskell, say, you won't have a large
number of the holes that C gives you. But for safety critical systems, their
rules say that you can't write code that has even the _possibility_ of falling
into those holes. That makes C much safer. That leaves you with a safer C,
plus tooling, vs. Haskell (or whatever your choice is) without the tooling.

So, snark aside, what is your actual alternative that you recommend?

~~~
lisper
> did you actually read the article?

Yes. I used to work with the person who wrote it.

> One of the reasons for C is that the tooling around it gives you much more
> support.

That's because C is an unsafe language, so to write safe code you _need_ that
tooling. If you start with a safe language the need for that tooling simply
evaporates because many of the mistakes that the tooling is designed to catch
are simply not possible.

> For safety critical code, you need that, because programmers make mistakes,
> no matter what the language.

That's certainly true, but many of those mistakes are mistakes in the
rendering of the desired semantics into code. Neither rules nor tooling can
catch those kinds of mistakes.

> But for safety critical systems, their rules say that you can't write code
> that has even the possibility of falling into those holes. That makes C much
> safer.

No, it doesn't. All of the rules that are not specific to C can be equally
well applied to any other language.

> So, snark aside, what is your actual alternative that you recommend?

Good heavens, just about _anything_ is better than C if what you care about is
safety. Ada. Java. Scheme. Haskell. OCaml. Python. Go. Swift. Common Lisp.

~~~
AnimalMuppet
Well, of those, Java, Haskell, and Lisp (at a minimum) are completely
unsuitable due to allocation and, worse, garbage collection. In a hard real-
time system those are a deal breaker. (Usually. You can have deterministic
allocators and guaranteed-worst-response-time garbage collectors, and then you
can still prove you can meet your timing requirements, but it's not easy. Or,
you can try to write those languages in a way that they _never_ allocate after
initialization, which might be possible, at least for Java.)

Python? Can't you get "method not supported" (or whatever the technical term
is within Python)? That's not safety, by any stretch of the imagination. Or is
there some way to prove that it can't happen, by some kind of (ahem) external
tooling?

Ada I could accept. Go and Swift might be suitable. I don't know enough about
Scheme or OCaml to comment.

~~~
lisper
> completely unsuitable

You are wrong. And you even _admit_ that you are wrong in your own comment:

"You can... but it's not easy"

There's a big gap between "completely unsuitable" and "not easy."

But you are wrong for an even more fundamental reason: the matter at hand is
not hard-real-time cade, it is _safety-critical_ code (re-read the title).
Those are not the same thing. Safety-critical code can be and has been written
in Common Lisp. Moreover, an effort to port the Common Lisp code to C++
failed. So it is manifestly untrue that Common Lisp is "completely unsuitable"
for writing safety critical code.

~~~
AnimalMuppet
The title says "safety critical code". But the article says no allocations,
_because of the unpredictable (time) behavior_. So the environment that the
article describes is safety-critical, and part of that is predictable
(guaranteed) timing.

> You are wrong. And you even admit that you are wrong in your own comment

Do you realize that you're agreeing with me? I put that part in parentheses in
for a reason...

~~~
lisper
> Do you realize that you're agreeing with me?

No. I am not agreeing with you. You are wrong about everything, even the real-
time part. You are even wrong when you say it's not easy to write hard-real-
time code in GC'd languages. It is no harder to do that in GC'd languages than
in non-GCd languages. In fact, it's easier. You are correct that it is hard to
write a hard-real-time GC. But that job only has to be done once, and it has
already been done so at this point it's a sunk cost. Writing non-consing code
is also not hard, certainly no harder than writing non-consing code in C.

Not only is Common Lisp suitable for writing safety-critical code, it
completely dominates C in every possible way. And this is not just
speculation, it has been actually demonstrated in the field.

Oh:

> But the article says no allocations, because of the unpredictable (time)
> behavior

You're wrong about this too. It does say it's because of the unpredictable
behavior, but the "time" part is your own extrapolation. The article itself
says nothing about hard-real-time. The word "time" does not even appear
anywhere. (Not that this matters because it is no harder, and often easier, to
write hard-real-time code in Lisp as it is in C.)

~~~
AnimalMuppet
> Writing non-consing code is also not hard, certainly no harder than writing
> non-consing code in C.

I call BS on this claim. To write non-consing code in C, what functions do I
need to avoid? malloc, calloc, and realloc. To avoid writing consing code in
Lisp, what functions do I have to avoid? Can you list all of them for me?

> Not only is Common Lisp suitable for writing safety-critical code, it
> completely dominates C in every possible way.

BULLSHIT. Your extreme departure from reality makes it hard for me to respond
any more politely than that.

Earlier, you cited a safety-critical system that had been written in Lisp. (I
believe that you've cited it before.) They tried to re-write it in C++, and
failed. You know what else I read on HN today? Banks have COBOL code that
can't be re-written in something better and/or more modern. But it's not the
wonderfulness of the language that's preventing it; it's that nobody
understands what the code does well enough to be able to write a true
functional equivalent. So, you've got a nice example, and a nice story, but it
may not prove the point that you're trying to prove.

Now, what else have you got? _How many_ safety critical systems are written in
Lisp? How many of those are hard real-time systems? What are their defect
rates compared to people writing MISRA C? And, if Lisp is so wonderful
compared to C, how come so many more safety-critical systems are written in C?
Is it because the entire profession is blind, and only you and a few
enlightened ones realize how wonderful Lisp is? Or is it because people that
have done this for decades see more problems with Lisp than you are aware of?

> It does say it's because of the unpredictable behavior, but the "time" part
> is your own extrapolation.

True. Now, in what ways do allocations behave unpredictably? Well, any
particular allocation can fail due to being out of memory, and the location
allocated is unpredictable. But the _primary_ way that allocations are
unpredictable is in the amount of time they take.

If you want to argue with that statement, along with your argument, tell me
how many years of your career you have spent in embedded systems. And don't
try to argue that embedded systems isn't what we're talking about here, just
because the title says "safety critical" rather than "embedded".

~~~
lisper
> how many years of your career you have spent in embedded systems

Depends on how you count, but probably about 20. 15 of those were at NASA.
Gerard Holzman (the author of the paper we're discussing) was in the office
next to mine for about 3 years. Oh, and Common Lisp code that I wrote actually
controlled a spacecraft back in 1999.

[https://ti.arc.nasa.gov/tech/asr/planning-and-
scheduling/rem...](https://ti.arc.nasa.gov/tech/asr/planning-and-
scheduling/remote-agent/experiment/)

Does that qualify me to talk about these things?

~~~
AnimalMuppet
> Does that qualify me to talk about these things?

Yes, certainly. (Though I have been in embedded longer - 25 years. But you've
been in safety-critical longer than me.) I must admit that I under-rated your
experience.

What part of the stuff from that link was written in Lisp? Was it just the
planner, or also the exec? That is, did it have to be real time?

And, in my previous reply, I asked a bunch of questions (besides the ones
about whether you knew what you were talking about). Would you answer them?

~~~
lisper
> What part of the stuff from that link was written in Lisp?

All of it. (Except for an IPC system which was written in C. See
[https://www.youtube.com/watch?v=_gZK0tW8EhQ](https://www.youtube.com/watch?v=_gZK0tW8EhQ)
if you want to know the whole story.)

> Would you answer them?

I'll answer a few of them.

> if Lisp is so wonderful compared to C, how come so many more safety-critical
> systems are written in C?

Politics mainly. And inertia. And the sunk cost fallacy.

> Is it because the entire profession is blind, and only you and a few
> enlightened ones realize how wonderful Lisp is?

Yes. Pretty much.

> Or is it because people that have done this for decades see more problems
> with Lisp than you are aware of?

They, like you, think they see problems, but they, like you, are wrong.

Tellingly, none of the people who think there are problems with Lisp actually
know Lisp.

~~~
AnimalMuppet
> Politics mainly. And inertia. And the sunk cost fallacy.

I would say no, sort of, and maybe. I don't think I've ever seen a language
chosen because of politics. For "inertia", I would say "conservatism" \-
people know that they can build systems (even safety critical ones) using C,
and they know where the problems are. They don't know where the problems are
using Lisp. And they don't want to take the time to learn Lisp - that's sunk
cost, but whether it's a fallacy or not depends on whether Lisp really is more
suited for such work than C. You assert that it is; many of us would like to
see considerably more evidence before we agree.

What evidence? Maybe a few hundred hard real time systems successfully written
in Lisp. (But how are we going to get them, if everybody defaults to C? I will
grant you that there's a chicken-and-egg problem here...)

> > Is it because the entire profession is blind, and only you and a few
> enlightened ones realize how wonderful Lisp is?

> Yes. Pretty much.

Yeah... um... I think I'll just let your words speak for themselves.

~~~
lisper
> chicken-and-egg

Yep.

~~~
technion
I want to follow up that whole discussion by stating I really appreciate
someone with actual experience in this area chiming in. NASA code attracts a
huge amount of bikeshedding here.

~~~
lisper
Thanks!

------
keithnz
These are basically a subset of rules that MISRA defines for C programming.

------
elcapitan
> Do not use [..] direct or indirect recursion.

Ok.. I get it that they don't want their C programmers to do that, but do they
also mean that this is to "complex" for normal developers to implement in a
fault-free manner?

~~~
corecoder
They mean that it's hard to write a static analysis tool that can determine if
functions using recursion will ever terminate or not.

~~~
elcapitan
Just out of curiosity, why is that harder than writing a static analysis that
determines if a loop with condition will finish?

~~~
Jtsummers
The sort of software these rules are intended for run in constrained
environments. The other question besides "Will this loop/recursion terminate?"
is "Will we blow up the stack?".

Ignoring the time required to establish a new stackframe (versus a mere jump
for a loop), the creation of a stack frame uses more memory. The less memory
you can use, while still producing maintainable code, the better. And an easy
way to assist with this is to remove recursion.

You could make the case for tail recursion, which a sufficiently advanced
compiler will/can/should turn into a mere jump, like a loop, because it is.
But this isn't guaranteed in the C standard and so should not be relied upon.

And then there's Rule #2. Loops are bounded, the index variable is supposed to
only be altered by the loop structure (so: `for(i = 0; i < MAX; i++)`, that
increment is the _only_ place `i` should be altered). By providing a compile-
time max, and incrementing (deterministically) the index variable, you ensure
that static analysis of the loop terminating (or not) is possible.

~~~
AnimalMuppet
And tail recursion is equivalent to a loop, _but a form of loop which breaks
Rule 2_.

~~~
Jtsummers
Tali recursion _should_ be equivalent to a loop, _if_ the language
compiler/implementation offers TCE/TCO. C does not guarantee this.

But I'll also point out, tail recursion could be statically analyzed in the
same way that you would with a for loop (for the purpose of Rule 2). As long
as it is structured in a way that demonstrates that:

1) Forward progress is always being made (that is, there's an iteration/index
parameter that always increases/decreases monotonically) towards a bound: void
f(state,n) { if(n > MAX) return; f(g(state),n+1); }

2) There's a single entry point which initiates the recursion (loop) with 0
(or MAX if you're decrementing, or whatever): void start(state) { f(state,0);
}

Both can be demonstrated (with code structured like the above) statically.

~~~
AnimalMuppet
All true.

A modified C compiler _could_ be built that would do TCO. But, for this kind
of application, it would mean that you'd have to re-validate your compiler, so
that's pretty much not going to happen.

~~~
Jtsummers
Looking it up, it seems that GCC (at least) presently does do (at some
optimization levels) tail call elimination. Others probably do as well. I
guess I've never looked it up because it was never relevant (if I'm using C, I
might as well use for loops, they're at least as clear as the equivalent tail
recursive function).

