I think that is the biggest factor of all.
I think that is the biggest factor of all.
Given the sophistication of the language and the compiler technology of the day, there was no way Ada was going to run well on 1980’s microcomputers. Intel built the i432 “mainframe on a chip” with a bunch of Ada concepts baked into the hardware for performance, and it was still as slow as a dog.
And as we now know, microcomputers later ate the world, carrying along their C and assembly legacy for the better part of two decades, until they got fast enough and compiler technology got good enough that richer languages were plausible.
The third validated compiler ran on the Western Digital “Pascal MicroEngine” running the UCSD p-system with 64K memory. The MicroEngine executed the byte code from the p-system natively, which was an interesting approach.
I think more research is warranted by you on this subject.
It might be nice to have Ada's tasks driven by DOS interrupts, though. I think GNAT did this.
"Shortly before we finished our compiler, Borland came out with Turbo Pascal, and they were considering going into the Modula-2 market as well. In fact, Borland decided to buy our Modula-2 compiler to be sold under the name of Turbo Modula-2 for CP/M alongside an IBM PC version they wanted to develop. We offered to do the IBM PC version for them, but they told us they had it already covered. Unfortunately that version took them much longer than planned. By the time it came out, three or four years later, their implementor team had split from the company, and it became known as TopSpeed Modula-2. In the absence of an IBM-PC version, Borland never put any marketing muscle behind Turbo-Modula-2, so it remained rather obscure." -- https://www.artima.com/articles/the-origins-of-scala
Note that the Martin Odersky of Scala fame was one of the main developers of Turbo Modula-2, that Borland killed shortly thereafter. They were also rather quick to get rid of Turbo Basic (which I had quite some fun with).
With Turbo Pascal absorving the Object Pascal ideas from Apple, and some of the key features from Modula-2, there was not much left from Ada 83 that was relevant for MS-DOS programmers.
However nowadays between FreePascal, Delphi, and Ada, I would probably pick Ada, given its industry role, and besides Ada Core, there are actually other six vendors still in business.
Ada includes a number of critical abstractions that require either dynamic runtime code (slow runtime) or the proverbial sufficiently smart compiler (slow compile-time).
These were for good reasons, like safety and the need to define concurrent systems within the language. But they were too heavyweight for the commodity hardware of the era.
Nowadays, languages like Go, C++, Java, Rust, … have no trouble with similar abstractions because optimizers have gotten really good (particularly with inlining) and the hardware has cycles to spare.
same is true today. the spec/body separation made for an actual delineation between design and implementation.
When the companies bought the Solaris Developer tools, that did not include the Ada compiler, that was extra, and wasn't cheap.
Having already paid for C, C++, Assembly, why would anyone pay extra for Ada if not obliged to do so?
It had other warts the string handling wasn’t great, which was a huge problem. It was slow too in a time where that mattered more (we had c and ada in our code base.). I remember the concurrency not using the OSs so the one place we used it was a pain. HPUX had an amazing quasi real time extensions, so we just ran a bunch of processes.
In that era, the largest blocker for Ada was it ws viewed as having a lot of overhead for things that weren't generally seen as useful (safety guarantees). The reputation was it only mattered if you were working on military stuff, etc.
Moreover, for a very long time GNAT had been quite difficult to build, configure and coexist with other gcc-based compilers, far more difficult than building and configuring the tool chain for any other programming language. (i.e. you could fail to get a working environment, without any easy way to discover what went wrong, which never happened with any other programming language supported by gcc)
I have no idea which was the reason for this, because whichever was the reason it had nothing to do with any intrinsic property of the language.
I do not remember when it has finally become easy to use Ada with gcc, but this might have happened only a decade ago, or even more recently.
not that it was their responsibility to provide a free compiler to the masses.
I think this should not be underestimated. There is a huge number of small C compilers. People write their own C compiler because they want to have one.
That doesn't happen we Ada. Very few people liked Ada enough that they would write a compiler for a subset of the language. For example, an Ada subset similar to the feature set of Modula-2 should be quite doable with a modest effort.
You're right but it's broader than "C folks like terseness."
C is famously hard to read. Before Perl we used to joke that C is a write-only language: you can't understand what your own code means just weeks later.
Combine this with its lack of bounds checking, pointer arithmetic, and other dangerous features, and the result is a language that's macho for geeks: it's hard, it's dangerous, but it's small and it's fast.
It's a motorcycle for nerds. Ada is a tank.
Nerds get to establish dominance over lesser nerds by doing hard stuff in hard languages and making it fast. This bestows nerd street cred: geek cred.
Ada was used by contractors who needed stuff to work and money was no object.
C was used by hackers to do cool hacker stuff that was perceived to be fast and low level.
It's not low level: machine architectures haven't resembled the C abstractions since the 1970s.
https://queue.acm.org/detail.cfm?id=3212479
A modern low-level language would be some brain-bending combination of APL and Lisp with n-dimensional tensor algebra or something.
But C looks cool and hard and you will blow both feet off if you don't hold it just right.
And there are good free versions. So you can be poor and still demonstrate your machismo.
Result, a software industry requiring weekly multi-gigabyte online patches, keeping millions in work.
C makes programmers a cheap fungible commodity.
* strong typing
* lots of annotations
* keywords over syntax, support for long variable and token names
* object focus (Ada 83 had some limitations on inheritance so it wasn't OO strictly speaking)
* exceptions
* large standard library
These things were controversial in the 1980s. They are not today.One of the big differences between K&R C and C89 is the introduction of function prototypes. Strong typing was certainly considered positive for compiled languages. Of course C is a lot less strict than Ada.
If we compare the Rust subset that has similar functionality as C then there is not much difference. You get 'fn'. The is 'let' but Rust often leaves out the type, so 'int x = 42;' becomes 'let x = 42;' in Rust. Rust has 'mut' but C has 'const'. Rust introduced '=>' and removed '->' from object access and moved it to the return type of a function.
The C language has support for long variable names. Some early linkers didn't, but that's an implementation issue, people were certainly unhappy about that.
C++ started in the 80s. Objects were not controversial back then. The same applies to exceptions.
I don't have a metric for the size of a standard library. For its time, the C library in Unix system had a large number of functions. Later that was split in a C standard part and a POSIX part. But that was for practical reasons. Lot's of non-Unix systems have trouble implementing fork().
I have no clue what you mean with annotations. If you mean non-function annotations along with code, then generally Rust programs don't have those.
The Ariane 5 crash involved an exception and that was the central "Ada is unsafe actually" argument from C people.
In fact "exceptions are bad" is so baked into a lot of C people's brains that they left them out of Go!
Short variable names were a technical limitation in early languages but style guides were still arguing against long, descriptive variable names in languages like C into the 2000s.
Objects were also likewise controversial and you can see that in the design of Ada 83 where they were both inspired by OO languages like smalltalk but also hesitant to adopt stuff like inheritance. Inheritance was again, seen as a way to break encapsulation (it kinda is) but also a lot of object implementations were slow and memory inefficient in the 80s. Smalltalk was pretty much the reason why the Apple Lisa failed as a product.
OO became a massive buzzword in the 90s but by that time it had already been around for quite a long time.
By annotations I mean mostly type annotations, of course there's also aspect annotations and other stuff ex: Ada SPARK.
Back when I learnt C, I think you could go beyond 8 or 12 characters in the symbol tables of compilers like Small-C.
Ironically the author of Fil-C calls classical C, YOLO-C. :)
in the 1980/1990's i was a dumb kid. problems of large systems were not in my mind. having to type begin/end instead of {} was, i thought, a valid complaint.
with experience, education, and hindsight, most of the advantages of the ada language were not understood by the masses. if ada came out today, it would have taken off just like rust.
Given how similar the syntax is of C, C++, Javascript, and Go, I think a language with the syntax of Ada would have a hard time.
I was around then, and culturally there just wasn't this (legitimate) concern with safety in the more "hacker" and Unix community generally. C won headspace at the time precisely because it was minimal and close to the metal while providing the minimum of abstraction people wanted. Which was on the whole fine because the blast radius for mistakes was lower and the machines were simpler.
Yes, I think this is key. I wasn't around in 1985, but on every attempt to write something in Ada I've found myself fighting its standard library more than using it. Ada's stdlib is an intersection of common features found in previous century's operating systems, and anything OS-specific or any developments from the last 30 years seem to be conspicuously absent. That wouldn't be so much of a problem if you could just extend the stdlib with OS-specific features, but Ada's abstractions are closed instead of leaky.
I'm sure that this is less of a problem on embedded systems, unikernels or other close-to-hardware software projects where you have more control over the stdlib and runtime; but as much as I like Ada's type system and its tasking model I would never write system applications in Ada because the standard library abstractions just get in the way.
To illustrate what I mean, look at the Ada.Interrupts standard library package [0] for interrupt handling, and how it defines an interrupt handler:
type Parameterless_Handler is
access protected procedure
with Nonblocking => False;
That's sufficient for hardware interrupts: you have an entry point address, and that's it. But on Linux the same package is used for signal handling, and a parameterless procedure is in no way compatible with the rich siginfo_t struct that the kernel offers. To wit, because the handler is parameterless you need to attach a separate handler to each signal to even know which signal was raised. And to add insult to injury, the gnat runtime always spawns a signal handler thread with an empty sigprocmask before entering the main subprogram so it's not possible to use signalfd to work around this issue either.Ada's stdlib file operations suffer from closed enumerations: the file operations Create and Open take a File_Mode argument, and that argument is defined as [1]:
type File_Mode is (In_File, Inout_File, Out_File); -- for Direct_IO
type File_Mode is (In_File, Out_File, Append_File); -- for Stream_IO
That's it. No provisions for Posix flags like O_CLOEXEC or O_EXCL nor BSD flags like O_EXLOCK, and since enum types are closed in Ada there is no way to add those custom flags either. All modern or OS-specific features like dirfd on Linux or opportunistic locking on Windows are not easily available in Ada because of closed definitions like this.Another example is GNAT.Sockets (not part of Ada stdlib), which defines these address families and socket types in a closed enum:
type Family_Type is (Family_Inet, Family_Inet6, Family_Unix, Family_Unspec);
type Mode_Type is (Socket_Stream, Socket_Datagram, Socket_Raw);
Want to use AF_ALG or AF_KEY for secure cryptographic operations, or perhaps SOCK_SEQPACKET or a SOL_BLUETOOTH socket? Better prepare to write your own Ada sockets library first.[0] https://docs.adacore.com/live/wave/arm22/html/arm22/arm22-C-...
[1] https://docs.adacore.com/live/wave/arm22/html/arm22/arm22-A-...
The best way to use Ada, IMO, is type-first: you define your problem-space in the type-system, then use that to solve your problem. -- Also, because Ada's foreign-function interface is dead easy, you could use imports to handle things in a manner more amiable to your needs/preferences, it's as simple as:
Function Example (X : Interfaces.Unsigned_16) return Boolean
with Import, Convention => COBOL, Link_Name => "xmpl16";
You can even put pre-/post-conditions on it.> The best way to use Ada, IMO, is type-first: you define your problem-space in the type-system, then use that to solve your problem
I guess that goes to the core of the argument I was trying to make: not that Ada is bad, but that the low-level abstractions in Ada's stdlib are a case of premature optimization. Luckily, I take much less issue with the Numerics and Container parts of the standard library.
> To use the posix-flags, you use the Form parameter
Do you have any examples/documentation on the use of the Form parameter? According to the RM, it's a String argument so I wouldn't have expected it to support flags.
(Also, to correct myself on the signalfd issue: there is GNAT.Signals.Block_Signal to mask signals on the Interrupt_Manager thread)
For GNAT, these two pieces of documentation are instructive: https://docs.adacore.com/live/wave/gnat_rm/html/gnat_rm/gnat... https://gcc.gnu.org/onlinedocs/gcc-4.9.1/gnat_rm/FORM-String... (This second one is older documentation, but illustrates how platform-specific Form parameters could be used.)
Ada.Text_IO.Create (
File => File,
Mode => Ada.Text_IO.Out_File,
Name => "test.txt",
Form => "shared=no"
);
The "maze of alternative package bodies and accompanying conditional compilation logic" is an artifact of C's approach to 'portability' using the preprocessor. Typically, the conditionality should be stable once you abstract it (using the compiler's project-management to select the correct body for a particular configuration) -- As a stupidly trivial example, consider the path separator, for the specification you could have: Package Dependency is
Package OS is
Function Separator return String;
End OS;
End Dependency;
-- ...
Package Dependency is
Package body OS is separate;
End Dependency;
-- Windows
separate (Dependency)
Package OS is
Function Separator return String is ("\");
End OS;
-- Classic Mac
separate (Dependency)
Package OS is
Function Separator return String is (":");
End OS;
-- VMS
separate (Dependency)
Package OS is
Function Separator return String is (".");
End OS;
-- UNIX-like
separate (Dependency)
Package OS is
Function Separator return String is ("/");
End OS;
Then in your the rest of your program, you program against the abstraction of DEPENDENCY.OS (and whatever other dependencies you have, likewise), and thus separate out the implementation dependency.Even in the late 90s Jamie Zawinski had a rant against C++. His argument for not using it? The compilers suck! C++ was the main "competitor" of Ada and it was a decade or more behind Ada through most of the time.
The "killer feature" of C++ against Ada (when it came to fighting against compiler maturity) was really that you could pretend to be writing C++ code but really just keep writing C-with-classes.
If Ada had put a modula or pascal compatibility mode in the language and produced a reference compiler that was based on a stable compiler in one of those languages, the history may have been different because people could have just written "PascAda" while waiting for the compilers to catch up.
I and many others have looked at Ada with some appreciation for decades. But the actual "community" around the language was foreign to me; government, defense contractors, etc places that frankly wouldn't even hire me.
It's got appealing constructs, and I grew up with the Wirth languages so I wans't put off by its syntax and style... and I even sometimes considered rewriting my OSS C++ pieces in it because I was so desperate for something better. But it was just a self-limiting box.
The concept of iAPX 432 had been finalized before Ada won the Department of Defense competition.
iAPX 432 was designed based on the idea that such an architecture would be more suitable for high level languages, without having at that time Ada or any other specific language in mind.
The iAPX designers thought that the most important feature that would make the processor better suited for high-level languages would be to not allow the direct addressing of memory but to control the memory accesses in such a way that would prevent any accesses outside the intended memory object.
The designers have made many other mistakes, but an important mistake was that the object-based memory-access control that they implemented was far too complex in comparison with what could be implemented efficiently in the available technology. Thus they could not implement everything in one chip and they had to split the CPU in multiple chips, which created additional challenges.
Eventually, the "32-bit" iAPX432 was much slower than the 16-bit 80286, despite the fact that 80286 had also been contaminated by the ideas of 432, so it had a much too complicated memory protection mechanism, which has never been fully used in any relevant commercial product, being replaced by the much simpler paged memory of 80386.
The failure of 432 and the partial failure of 286 (a very large part of the chip implemented features that have never been used in IBM PC/AT and compatibles) are not failures of Ada, but failures of a plan to provide complex memory access protections in hardware, instead of simpler methods based on page access rights and/or comparisons with access limits under software control.
Now there are attempts to move again some parts of the memory access control to hardware, like ARM Cheri, but I do not like them. I prefer simpler methods, like the conditional traps of IBM POWER, which allow a cheaper checking of out-of-bounds accesses without any of the disadvantages of the approaches like Cheri, which need special pointers, which consume resources permanently, not only where they are needed.
Where it went wrong is in two areas: 1) as far as I know the 286 does not correct restart all instruction if they reference a segment that is not present. So swapping doesn't really work as well as people would like.
The big problem however was that in the PC market, 808[68] applications had access to all (at most 640 KB) memory. Compilers (including C compilers) had "far" pointers, etc. that would allow programs to use more than 64 KB memory. There was no easy way to do this in 286 protected mode. Also because a lot of programs where essentially written for CP/M. Microsoft and IBM started working on OS/2 but progress was slow enough that soon the 386 became available.
The 386 of course had the complete 286 architecture, which was also extended to 32-bit. Even when flat memory is used through paging, segments have to be configured.
Nobody has ever used the 286 "protected mode" in the way intended by its designers.
The managers of "extended memory", like HIMEM.SYS, used briefly the "protected mode", but only to be able to access memory above 1 MB.
There were operating systems intended for 286, like XENIX and OS/2 1.x, but even those used only a small subset of the features of the 286 "protected mode". Moreover, only a negligible fraction of the 286 computers have been used with OS/2 1.x or XENIX, in comparison with those using MS-DOS/DR-DOS.
Ada can be implemented on any processor with no particular difficulties. There are perceived difficulties, but those are not difficulties specific to Ada.
Ada is a language that demands correct behavior from the processor, e.g. the detection of various error conditions. The same demands should be made for any program written in any language, but the users of other computing environments have been brainwashed by vendors that they must not demand correct behavior from their computers, so that the vendors could increase their profits by not adding the circuits needed to enforce correctness.
Thus Ada may be slower than it should be on processors that do not provide appropriate means for error detection, like RISC-V.
However that does not have anything to do with the language. The same problems will affect C, if you demand that the so-called undefined behavior must be implemented as generating exceptions for signaling when errors happen. If you implement Ada in YOLO mode, like C is normally implemented, Ada will be as fast as C on any processor. If you compile C enabling the sanitizer options, it will have the same speed as normal Ada, on the same CPU.
In the case of Rational/R1000s400, besides the fact that in must have had features that would be equally useful for implementing any programming language, it is said that it also had an Ada-specific instruction, for implementing task rendez-vous.
This must have been indeed helpful for Ada implementers, but it really is not a big deal.
The text says: "the notoriously difficult to implement Ada Rendez-Vous mechanism executes in a single instruction", I do not agree with "notoriously difficult".
It is true that on a CPU without appropriate atomic instructions and memory barriers, any kind of inter-thread communication becomes exceedingly difficult to implement. But with the right instructions, implementing the Ada rendez-vous mechanism is simple. Already an Intel 8088 would not have any difficulties in implementing this, while with 80486 and later CPUs maximum efficiency can be reached in such implementations.
While in Ada the so-called rendez-vous is the primitive used for inter-thread communication, it is a rather high-level mechanism, so it can be implemented with a lower-level primitive, which is the sending of a one-way message from one thread to another. One rendez-vous between two threads is equivalent with two one-way messages sent from one thread to another (i.e. from the 1st to the 2nd, then in the reverse direction). So implementing correctly the simpler mechanism of sending a one-way inter-thread message allows the trivial implementation of rendez-vous.
The rendez-vous mechanism has been put in the language specification, despite the fact that its place would have better been in a standard library, because this was mandated by the STEELMAN requirements published in 1978-06, one year before the closing of the DoD language contest.
So this feature was one of the last added to the language, because the Department of Defense requested it only in the last revision of the requirements.
An equivalent mechanism was described by Hoare in the famous CSP paper. However CSP was published a couple of months after the STEELMAN requirements.
I wonder whether the STEELMAN authors have arrived at this concept independently, or they have read a preprint of the Hoare paper.
It is also possible that both STEELMAN and Hoare have been independently inspired by the Interprocess Calls of Multics (1967), which were equivalent with the rendez-vous of Ada. However the very close coincidence in time of the CSP publication with the STEELMAN revision of the requirements makes plausible that a preprint of the Hoare paper could have prompted this revision.
Granted, some were pretty cheap, at least by the early 90s.