Advent of Computing - Episode 148 - Is BLISS Ignorance?

Episode Date: December 22, 2024

In 1970 a little language called BLISS emerged from Carnegie Mellon University. It was a systems language, meant for operating systems and compilers. It was designed, in part, as a response to Dijkstr...a's famous Go To Considered Harmful paper. It had no data types. It used the most bizzare form of the pointer I've ever seen. And it was a direct competitor to C. Sound interesting, yet? Selected Sources: https://bitsavers.computerhistory.org/pdf/dec/decus/pdp10/DECUS-10-118-PartII_BlissReadings_Dec71.pdf - Readings on BLISS https://www.cs.tufts.edu/~nr/cs257/archive/ronald-brender/bliss.pdf - A History of BLISS

Transcript
Discussion (0)
Starting point is 00:00:00 There are a few technologies that mark out key points in the history of the computer. These are keystone events that set the stage for the future. I'm talking things like the EDVAC report, which leads to a recognizable form of stored program computers. Or the launch of the IBM PC, which sets in stone what home computers would look like for decades to come, or IBM's System 360, which pushed us into the 8-bit byte as a standard. If any of these events happened slightly differently, well, the story of the computer would be very
Starting point is 00:00:37 different indeed. One of these keystone events happens in 1972. That's the creation of the C programming language. Almost every programming language in modern use is somehow related to or influenced by C. It's how curly braces become popular. It's why, if you know Python, you can pretty quickly pick up JavaScript. It's the root of so many current lineages. I think you'd be right to say that if C never happened, programming would look and be very different. Then, what could programming have looked like without C?
Starting point is 00:01:19 Now, that's really hard to say. C becomes a phenomenon for a number of reasons. However, there were a number of languages that were developed around the same time as C and had very similar design goals and purposes to that of C. These were high-level system programming languages. Tools meant for writing operating systems and compilers. These powerful compromises between the control of machine code and the flexibility of a high-level language. C itself evolves from some of these earlier attempts, but there are separate lineages here unrelated to this popular language. It's easy to imagine a history where C is developed but never takes off,
Starting point is 00:02:07 where it's killed in the cradle by one of these unrelated systems languages. In that case, the future of programming may have taken on aspects of the victor, that is, if we assume that this one fight in the 1970s leads to modern programming. If we follow that assumption, then looking at C's competitors could give us a glimpse of that alternate future. Welcome back to Advent of Computing. I'm your host Sean Haas, and this is episode 148, Is Bliss Ignorance? This episode will be taking a look at a failed language. Now, I will say a lot of failed languages aren't that interesting. There are some that are just idle passing fancies. There are some that are just amateurish. are just idle passing fancies, there are some that are just amateurish.
Starting point is 00:03:10 Not cool except for maybe a little pointing and laughing, but even that's in bad taste. This language, however, I think stands closer examination. The language we'll be discussing today is called BLISS. It was developed in 1969 at Carnegie Mellon University. It was intended as a portable systems programming language. It was derived from ALGOL. It competed directly with C, and it lost. What I find interesting is that the goals of BLISS align almost word for word with the
Starting point is 00:03:43 goals of C. The key difference from the outside is that C was developed at Bell Labs. But there must be some deeper difference here, right? Why did bliss fall to the wayside as C rose to prominence? Was it a matter of place and time, or was bliss somehow missing a killer feature that made C a winner? In this episode, I want to talk about Bliss as its own language, but at the same time, I want to work out what made it different. Why was it considered a competitor to C? What did Bliss do that was better than C? What made it worse? And how did it eventually lose? In doing so, I hope we can work out a few
Starting point is 00:04:25 things about good program language design. But for this one, I'm going in totally blind. So who knows, maybe it's that C is the bad language and Bliss was wrongly overlooked. Why does that matter? Well, it all comes down to the power of C. Most modern languages are somehow related to C. If you've ever seen a language with curly braces, chances are it is directly related to C. The life of a modern-day programmer is very deeply shaped by choices made during the development of the C programming language in the early 1970s. That makes the idea of a counterfactual pretty darn exciting. What if C failed?
Starting point is 00:05:12 What if it was different? Or what if it was beaten out by an entirely different language? That language very well could have been Bliss. Now before we get into the episode proper, I have actually three announcements this time. The first is that I'm currently in the process of working up a bonus episode over on Patreon. Right now I have a thread up where I'm taking suggestions for a bonus episode for I think maybe another week, maybe two weeks, depending on how active it is. I'm then going to take the three most popular ideas in that thread and put it to a poll for the patrons.
Starting point is 00:05:45 There's still a little time to get in on deciding the bonus topic. For that you can head over to my Patreon. Announcement two is that on February 8th, I'm going to be speaking at Intelligent Speech. This is an online conference for history podcasters to get together and give talks on a specific topic. This year the topic is deception. It should be a lot of fun. I spoke last year and had a great time. So if you want to join in on that, you can get tickets at IntelligentSpeechOnline.com. And announcement number three is that I'm gonna be at VCF SoCal this February. I know, I'm making the rounds for once
Starting point is 00:06:34 VCF SoCal is happening February 15th through 16th in Orange, California It's right near LA you can actually fly into Burbank or LAX and get over to the conference pretty easily That event is taking place at the Hotel Farah, which is a big conference center. If you want to attend that, you can actually get special room rates through vcfsocal.com to stay in the same hotel and walk right down to the convention floor. It's actually a really, really nice setup. Right now I'm planning to give a talk on some software restoration at VCF SoCal.
Starting point is 00:07:03 I'm also going to be milling around the conference all weekend. So if that sounds fun, I'd love to hang out. Come show up, we can meet up, and we can have a great time. That once again is happening February 15th and 16th in Orange, California. Okay, all of that done, so let's start the episode itself. Computer programmers are a little, well, let's say dramatic. That might be the most reasonable word to use here. I think this has something to do with how programming kind of warps the human mind.
Starting point is 00:07:41 Because of that, we tend to get some pretty spicy papers and letters to the editor of various journals and magazines. One of the all-time greats in this genre is Edgar Dijkstra's go-to statement considered harmful. The paper hits print disastrous effects, and I became convinced that the go-to statement should be abolished from all higher-level programming languages, end quote. Dude doesn't like the go-to, and he argues that you should also hate it. But what exactly is the deal here? The go-to, on its own own is a pretty innocuous concept.
Starting point is 00:08:29 It tells a program to jump to some predefined label. You label some piece of code, then you tell the program, hey, you remember that part of the program I told you about? It's time to go to that. That simple. This is a very low level way of thinking about programming. It's actually how computers think. On the processor level,
Starting point is 00:08:51 most logic is built out of a series of what are called unconditional jumps. That's the exact same as a go-to. This is backed up with some conditional jumps, which are like having a go-to inside an if statement. Early on, many high-level languages also supported this type of jumping. Fortran, for instance, lets you run a go-to. Basic is, perhaps, famous for its use of the go-to statement. On its own, a go-to is fine. Well, on its own with a label around, but you get the idea.
Starting point is 00:09:26 Where we get into dicey territory is once we start looking at everything that can be around the go-to. For a very simple program, a jump is fine, it's just a jump. What you often want is to only execute a part of a program if some condition is met, and otherwise execute some other part of that program. For that, you can chain together some if statements with a few go-tos. If the light is green, then go to the label called start. If the light is red, then go to the part of the program called stop. Very simple.
Starting point is 00:10:01 But what happens if, say, you have a go-to in a more complex program? Let's say, by way of example, you have a loop that contains a go-to statement. How should the language handle that? You're basically terminating that loop early and going to some other part of the program. That's perhaps a little strange, but we can probably all work up a solution to that problem, right? You're just exiting that loop a little early, but what happens to variables inside that loop?
Starting point is 00:10:34 What happens to the scope of variables when you jump? What happens to global variables that have been modified when you jump outside a loop? You get into all these tricky gotchas that you have to think about and plan around in the language. Now let's flip that example. Let's say you have a label inside a loop. Then you jump to that label. You're jumping right to the middle of a loop, effectively entering the loop from the side. But it's not that simple. Loops all have to start with some kind of setup routine. It's usually just a header that says, do this while x is less than 10, or something similar. If you
Starting point is 00:11:17 jump to the middle of a loop, then you are, in effect, entering that thing prematurely, the setup hasn't been done. So what happens? If the loop is supposed to run 10 times, but you jump into the middle, does it run 10 and a half times? What if the loop assumed that x would start at 0, but you're jumping from some point where x is 1000? Or x is minus 10, or x is some other non-numeric value. Suddenly you have to design a pile of edge cases or
Starting point is 00:11:52 build out a robust error handling system, or maybe have go-tos that can jump anywhere, but no not actually there, you have to have some restrictions. Go-to considered harmful was published in Communications of ACM and quickly became a hit. Over the years, it's become something of a meme amongst programmers, but its core idea is sound. Go To was implemented in most languages prior to 1968, and it introduces a host of problems and inconsistencies.
Starting point is 00:12:21 This isn't to mention things like optimization, which becomes almost impossible if you don't know the flow of a program. One of the people impacted by Dykstra was one Dr. William Wolfe, who at the time was a researcher at Carnegie Mellon University. As Wolfe would later explain in an oral history interview, quote, I bought into that and decided we should design this language, Bliss, without the go-to statement.
Starting point is 00:12:50 It was clear theoretically that you could do without it as long as you had other control structures like if, then, else, and then various iteration statements and subroutine calls, but it wasn't clear that as a practical matter that was going to be a good idea." Now, to be 100% clear here, there were already some languages without go-to statements. Lisp and its family offer one example. That said, Lisp-like languages have always existed kinda in their own world. They're purely functional, which is a totally different approach to design. Most other languages, from Fortran to Algol, from BASIC to PL1 to COBOL, used the go-to. The idea of a language built without go-to that wasn't a
Starting point is 00:13:40 Lisp relative was an enticing line of inquiry. But that wasn't Wolf's only motivation. The Dykstra paper would serve as something of a jumping-off point to get Wolf into a larger project he had been considering. In this same period, there was a hot debate over programming operating systems. This is sometimes called systems programming, that is, very complex code that sits close to the computer itself. The key argument was, should operating systems be programmed in high-level languages? For the 1960s, this was high controversy. It was traditional to write operating systems in assembly language or machine code. This was done at first because you just didn't have any other options. High-level languages
Starting point is 00:14:29 don't really appear until the late 1950s. When they do, they aren't very well suited to systems programming. Why wouldn't a high-level language work for this application? It all comes down to control. Systems programming requires very precise code and total control over the computer. You have to do things like juggle registers, move around blocks of memory to precise locations and handle physical interfaces. The whole point of a high-level language is to pull the programmer out of all those mundane details. A language like Fortran was designed to protect you from all sorts of low-level nasties. But it's that exact nastiness that you have to face
Starting point is 00:15:14 when you write an operating system. This leads to what seems like a paradox. High-level languages are meant to stay out of the low-level realm of the machine. So how do you use a high level language to write low level code? What's even the point of attempting such a feat? There are a lot of good reasons to break from tradition and use a high level language for this kind of software. I know this from experience. I've written a few very bad operating systems
Starting point is 00:15:46 in my day in both assembly language, a low-level language, and C, a high-level language. When you work in a high-level language, you can use less code to do the same thing. Fewer lines of code means fewer bugs, and it means that the bugs you introduce are easier to track down. You also get access to much more sophisticated features. This varies language to language. For me, the jump from assembly to C gave me reusable data structures, data types, and actually visible pointers. That's not to mention that high-level languages are just more readable and easier to maintain. By 1968, there were a number of projects that were attempting to make this jump. Burroughs
Starting point is 00:16:31 had an operating system written in a dialect of Algol. So did GE. There was also the ongoing Multics project, a full time-sharing operating system written in PL1. We're solidly in the emerging phase of this technology, and Wolf wanted in. Sometime around 68, he started his plans for a new operating system. The first step, though, was choosing the high-level language to use. After a survey, he landed on an exciting conclusion. None fit his criteria. As he would later write in his first paper published on Bliss, quote, we have chosen to add to the tongues of Babel
Starting point is 00:17:12 by defining yet another new language and some justification is required. The only valid rationale for creating a new language is that the existing ones are inappropriate to the task. It was our judgment that no existing language are inappropriate to the task. It was our judgment that no existing language dealt with all the proper issues and hence a new language was necessary." End quote. The go-to-less aspect being front of mind made its way into the list of requirements. I honestly think that might be one reason that Wolf really chose to create a new language.
Starting point is 00:17:46 All the competitors had go-to. The rest of the list has to do with systems programming, pure and simple. But keep in mind, this was an emerging field. There wasn't some traditional and enshrined set of requirements for this kind of language. There is plenty of space for discovery here, which is what makes these kind of times so exciting. Here I'm gonna be cherry-picking from some of Wolf's line items, because in his paper on Bliss he gives a straight-up list of design requirements. That's really handy to use. The first item, and probably the most obvious is quote access to all
Starting point is 00:18:27 relevant hardware features. To do systems programming you need to have some way to control the system. That's a simple enough idea right? But what does that mean in practice? This usually means allowing some kind of direct memory access. Yet again memory is the heart of the computer. But this isn't just to let you push around bits and make funny little images. Many computers represent I.O. devices in memory, or have special structures in memory for setting up things like interrupts or data channels. So if you have fine-grained control over memory, you have
Starting point is 00:19:05 nearly total access to the hardware of the machine. That's the route that Bliss would go. The next line item is that a system language should have no need for runtime support. What does this mean? Well, most normal languages are targeted to run under something like an operating system. That means that the code, once compiled, expects certain tools to be ready and waiting for it. If your program prints something to screen, it's using runtime support.
Starting point is 00:19:35 It's calling out to the operating system while it's running and asking for a little help. But what happens if you're the operating system itself? What if you are the clown pagliacci? In OS land, you have no runtime support. You have to bring your own runtime support. You have to make yourself laugh somehow. So a systems language cannot rely on runtime support. That means you won't find things like print in bliss. You have to work out how to do that yourself. The next item is control of representation. This goes hand-in-hand with hardware access. When a computer offers memory mapped stuff, that doesn't just mean location matters, it also means that
Starting point is 00:20:22 structure matters. An IBM PC, for instance, wants a very specifically formatted table at a very specific address for setting up interrupt handlers. How does control of representation help this? What does that even mean? Think about this in terms of data structures. If you have control of representation, then you can define data structures with a very specific pattern in memory. Then you can create a structure that matches that PC's interrupt table. You could graft that table from the computer's spec sheet into your language and then manipulate
Starting point is 00:21:00 it using your language. Sometimes you'll hear this concept, the representation of a data structure called its shape. At least, that's the term I use at work. Those are the straightforward and obvious line items, but the list keeps on going. This is where we pass into more innovative territory. The first is modularity. That is, chunks of code can be compiled into modules that can later be loaded by another program. On one hand, this is just practical program design. Modularity lets you reuse little helpful programs that are already compiled and
Starting point is 00:21:40 ready to grab. You just take the binary, plug it into your new program, and you're ready to go. Now, the documentation around modules in Bliss is a little cagey, and I don't wanna go down the route of learning and working with Bliss right now, so I'm gonna guess. The reason I said that modularity is an innovation here is because I think Bliss
Starting point is 00:22:05 can pull a trick with modules. Keep in mind this is from reading papers and spec sheets and extrapolating based off what I've seen in similar languages. I think Bliss should be able to do dynamic module loading. At least my understanding points to Bliss having all the features needed for dynamic modules. That means that while running, a Bliss program could grab a module from disk, load it into memory, and plug it into a running program. How Bliss handles communication in and out of modules, plus some of the weird stuff about its pointers, make me think that that kind of module loading would be pretty simple in the language. But hey, this is my guessing. There'd be a lot of actual support you'd have to write to do that. Now, the next design feature is conditional compilation. This one is also weird. The general
Starting point is 00:23:02 idea is that you have some way to control what the compiler chooses to compile. You can put in some special code that, given some information at compile time, will either be included or excluded from the final program. On the surface, why would you want that? You always want all your code compiled, right? Well, not necessarily. The most simple case here is debugging code. It's common to have a complex debugging library built up
Starting point is 00:23:31 that helps you debug your program. That can take up a lot of space and add features that only you as the programmer need. When it comes time to compile and ship your program to users, you don't wanna include all your debug code. With conditional compilation, you can just tell the compiler to ignore that part of the program. Thus, your dirty secrets are kept safe from prying eyes.
Starting point is 00:23:56 Bliss takes this a step further. It introduces this concept of a compile time code and runtime code. As in, code that's executed while the compiler is working and code that's executed when your final program is running. This is something that we don't have enough time to get into, sadly. The short story here is that Bliss has a very, very sophisticated macro system. It's so complex that you can write an entire program only using macros. That's, like I said, very complicated. It's something I'm choosing to gloss over in the interest of time. Now, the last item I want to hit
Starting point is 00:24:40 on is actually a sublist just titled, overall good language design. I mean, yeah, sure, all languages should have overall good design, right? The details here are pretty standard, things like readability and well-structured code. I want to just drop one thing from that list that I think is particularly interesting. Quote, economy of concepts, generality, flexibility. End quote. To me, that sounds like a concept we've talked about before. Orthogonal design. The idea was core to the design of Algold 68. In short, in orthogonal design, you try to have a minimized set of primitives in your
Starting point is 00:25:25 language that you use together to build up the rest of the programming language. To do this, you need to pick simple primitives that can work together. Algol 68 is very, very orthogonal. Like I said, it was a core design goal. Bliss isn't quite at that level, but the language has that orthogonal feel to it. In fact, in later papers, Wolf even mentions that he was trying to go for an orthogonal design. Now there is one other secret design goal. That's machine independence. That right there,
Starting point is 00:26:04 that should set off some alarm bells. How do you make a system's programming language that can be independent of the underlying system? Besides the paradox that introduces, it also puts bliss on a direct road to conflict with another language. But that conflict is still a few years off when Wolf decides on these design goals. When you write in assembly language, the main thing you're actually programming is memory manipulation. You might have some cool game or tool in mind.
Starting point is 00:26:38 You might have some neat algorithm you want to implement. But at the end of the day, the majority of your assembly code is just pushing around bits and building data structures in memory. When you're down in that low-level world, you don't get much help with, well, really anything. One of my favorite realizations came a few years ago. I usually write assembly language for the PC without any sort of runtime environment. I prefer the bare metal, as it's called. That means no Microsoft DOS, no real libraries, nothing. Well, I decided to write my first DOS program a few years back, still in assembly, of course. Now, DOS offers some useful runtime support. It will print to the screen for you. It will read
Starting point is 00:27:28 and write files from disk. All the classics. What it doesn't give you, however, is any kind of help pushing around bits. Even with DOS's runtime, you still have to manually handle memory. I remember realizing that, chuckling, and going back to my bit-bashing ways. Yet again, memory is key. This is why it's so crucial. It's such a big deal when a language supports data structures.
Starting point is 00:28:01 I personally like the dumb bean counting I have to do in assembly language because I don't do it professionally. It's a fun little hobby to do, like how some people paint minis or work on a car. But when it comes to actual programming, it's a pain. It's error prone, it's annoying, and it's very difficult to do correctly. When you jump to a high level language, you can just have, I don't know, an integer variable or a string or something more complex. That's a huge leap forward.
Starting point is 00:28:34 You don't have to worry about the beans anymore. All this is to say that data structures are very important. Variables and types are very important. Variables and types are very important. Bliss has no data types. It's what's known as a typeless language. What's interesting is that I can only think of a few other typeless programming languages. You have Forth, BCPL, B, and this weird cursed thing called PlanCalcool. To say it's an uncommon choice is an understatement. Typelessness is rare, strange, and wondrous to behold.
Starting point is 00:29:22 This gets weird quick, so I want to start from basics. I know I keep calling back to the Zeus episodes, but I can't help it. Zeus has managed to worm into my brain, so to everyone that said I should have covered Zeus much earlier, you were right, you win. Now I'm cursed. In PlanCalcool, Zeus's programming language, there's only one data type, the bit. You start from there, and you're expected to build up data structures by composing bits. That might make a structure called an integer that's composed of 32 bits, and you might treat that like a whole number. But the language itself has no enforcement of that structure.
Starting point is 00:30:07 It has no concept that an integer is 32 bits. That enforcement part is crucial here. That integer structure that you create isn't anything special to plancalcul. There's nothing stopping you from treating it like a 16-bit structure by accident or a character or something else that would make no sense at all. Bliss's type-free system works in a very similar way. It's not that you have no types, it's just that you only have one. In Bliss, that's the so-called full word. It's, well, just as it says. It's whatever the host computer uses as a word of data in memory. For the PDP-10, for instance, a full word would be 36 bits.
Starting point is 00:30:58 When you define a variable, it's allocated as a single word. You just get 36 bits. You, as the programmer, are expected to use those words as building blocks to make more complex data structures. If you read up on Bliss, one thing that's stated over and over again is that interpretation of values isn't related to the variable, but to the operators you apply to that variable. That's one way to deal with types, I guess. What that means is that instead of Bliss knowing, say, the difference between adding integers
Starting point is 00:31:38 and floating point numbers, you have to use special operations for each case. Bliss comes with an addition operation that can handle full word integer values, but no concept of what adding a decimal point means. If you want to do that, you need to write an operation or function that could treat a variable as a decimal and handle the actual math itself. There's no language level enforcement.
Starting point is 00:32:05 The rule of thumb here is just don't write nonsense code. And yes, we should always strive to produce correct code. That goes without saying, but the lack of support here feels, well, it feels more than a little bit concerning. If we stopped here just with the typeless variable system, then we'd be in quite a strange spot. Bliss would be a high-level language where you have to do assembly style bean counting to handle data. What makes this even more strange and perhaps once again wondrous is how Bliss handles pointers. I have to get a little technical here, so be warned.
Starting point is 00:32:48 Pointers give a high level language a way to talk about where data is stored in memory. That's crucial for any language like C or Bliss that has to precisely control what's in memory that goes back to the point about hardware control. In most languages, an assignment is defined to be something along the lines of symbol equals expression, where symbol is some valid variable or pointer or some named thing. And expression is any valid code that returns a value.
Starting point is 00:33:21 An assignment will take the value from that expression and put it in that specified symbol. It makes an association. Bliss defines an assignment as expression equals expression. Confused? Concerned? I was both when I saw that definition. The reason this works is that in Bliss, an unadorned variable is treated as a pointer, as is the left-hand side of an assignment. If you see a variable named X, that's actually a pointer that points to some full word in memory.
Starting point is 00:34:02 You have to do a special operation to get the value that pointer points to. In other words, when Bliss sees an assignment operation, it evaluates the first expression, the left-hand side, and treats that result as an address in memory. Then it evaluates the second expression, the right hand side, and stores its results in the specified address. This means you can do wacky and wild things like telling Bliss to store 1 in 1. That's just 1 equals 1. Or that x plus 1 equals 2.
Starting point is 00:34:39 That would be valid code. Now, I ask you, how does that make you feel? I would assume that for many practitioners, this should feel scary. Maybe it makes you mad. Like I said, this confused me at first. Why would you write a language like this? Well, dear listener, as I've learned more about Bliss, I think I've started to like this idea. But just to be clear, my point of view is a little twisted from exposure to bizarre programming languages. By allowing for expressions on the left-hand side, you open up a world of possibilities.
Starting point is 00:35:22 The big one is indexing. I want to reach for a real world example here. To do that, I want to introduce another feature of bliss. To get the value of a variable, you use the contents of operator. It's just a period. So you have a variable named X, then X on its own is just a pointer to memory. But dot X is the value that's stored at that address. On computers like the PC, you have a special region of memory for graphics. In text mode, this is just a chunk of memory that holds character data.
Starting point is 00:35:59 To print to the screen, all you need to do is store a character somewhere in that region of memory. If you want to print a string like hello world, you have to do is store a character somewhere in that region of memory. If you want to print a string like hello world, you have to use a loop. You start at the beginning of graphics memory and place the H, then you move to the next address in memory and the next letter in the word and print E and so on. One way to do this is to set up a pointer at the base of graphics memory, and then increment the pointer as you jump through the loop.
Starting point is 00:36:28 That's a very common solution in assembly. But Bliss offers another way. Since you can use a left-hand expression, you can write to memory with an offset. If you're inside a loop, you usually have some value, usually called I by tradition, that says how many times you've gone through that loop. You can just say something like memory plus I equals whatever next character you want to print. I think the actual expression you'd want to use would be graphics memory plus dot I equals string plus dot I that would Synchronize little indexes and by running that through you just print hello world to the screen
Starting point is 00:37:14 That may sound subtle and pretty technical The point here is that bliss's strange pointer system gives you more ways to solve problems It gives you different ways to solve problems at It gives you different ways to solve problems. At the same time, it introduces something funny. That offset trick I described is another commonly used idiom in assembly language. In fact, the whole expression pointer business is pretty common in assembly. This is a case where Bliss, once again, is acting like a low-level language. To be clear, this is a shared quirk amongst all system programming languages. You have to find some way to balance high-level features and hardware control. Bliss does it with this strange type and pointer system. You have the ability to use low-level tricks inside a high-level
Starting point is 00:38:06 language. But it's not just doing the same thing as assembly. Bliss is actually a market improvement here. The offset trick is a good example. In assembly language, you have very limited ways you can use offsets like that. You're usually restricted to offsets by constants or offset math using specific registers. You get a little flexibility, but not much. In Bliss, however, you can do any kind of math you want. You could call a function on the left-hand side. You could do a division operation or a modulo. In that sense, Bliss brings a whole lot of power to this lower level practice. Okay, so that's the most basic core of Bliss. From here, it continues to unfurl. I said that Bliss had a certain orthogonal feel to it. By that I mean all its features work with each other
Starting point is 00:39:06 to build upon each other. So let me unfold the next petal. Bliss is an expression language. That means that the smallest unit of code is an expression, as in everything evaluates to some value. That makes sense in most cases. Two plus two is an expression that evaluates to four. As we've seen, dot x is an expression that evaluates to the value of a certain address in memory. But everything is an expression. So x equals one is also an expression
Starting point is 00:39:44 that happens to evaluate to the number 1. In general, Bliss has evaluation rules for everything. That includes if statements, loops, declarations. It's everything, every construct in the language. If you write a line of code, it will return some defined value. This is in contrast to statement-based languages like Fortran or C or Python or... most other languages out there, actually. An assignment in Fortran doesn't have a value. It just tells Fortran to run an assignment.
Starting point is 00:40:20 You can't say x equals if blah. That would be nonsense. But in Bliss, an if statement is just an expression. And an assignment is just an expression, an equal sign, and an expression. Once again, the language is able to really work well with itself. So far, this should all sound pretty interesting, if not a little miserable. As I said, one of the big benefits of a high-level language over assembly is data management. Everything I've already covered, especially the centrality of the word, puts Bliss just
Starting point is 00:40:59 slightly above the level of assembly. You have some nice tricks with expression assignment, but not a lot more. What kicks things up a notch are structures. Now of course, Bliss has to do this its own way. To get this part to have the proper impact, I have to jump forward a little bit and touch on the elephant in the room. I've been trying to hold off talking about C, but to explain why Bliss's structures are so strange, I just gotta do it. In C, a structure is a way to define a custom data type composed of multiple values.
Starting point is 00:41:37 Suppose you're writing a program that deals with data on a grid. You'd wanna have some way to represent a data type for a point on that grid. You'd want to have some way to represent a data type for a point on that grid. You could just make a new structure, name it Point, and say it's composed of two integers named x and y. You can make new variables of type Point and easily access and set their x and y values. When you define a structure, you're also defining its layout and memory.
Starting point is 00:42:06 Our point structure takes up 8 bytes in RAM, 4 bytes for the x value, followed by 4 bytes for y. Every new point will take up the same amount of space and have the same layout. This is one of the ways that C handles precise hardware control. You can use structures to talk about memory in a high-level way. C structures are, for lack of a better term, declarative. You declare you have a new struct named point and you declare what it looks like. Bliss's structures are usually described as algorithmic, But I think it would be more in line
Starting point is 00:42:46 with the theme of language to call them expressive. In Bliss, you define a structure by specifying its access pattern. You name your struct, a list of arguments, and then an expression that uses those arguments. That is, on a fundamental level, pretty different than how other languages handle structs. Right away, something like the point struct is out the window.
Starting point is 00:43:10 You don't have named members in Bliss. The most basic example is an array of words. For that, you'd make a new structure, call it array, and say that it took one argument. Let's call the argument i since it's tradition. The access expression would be something like array plus dot i. That does the pointer math needed to get the i-th element of the array. Then you can make an array and access it
Starting point is 00:43:40 just like in any other language. Since the access expression can be any expression, you can describe very complex data structures. You can make multi-dimensional arrays, or trees, or anything. What's crucial, and what's so different here, is that you aren't defining the shape of the structure. Rather, you're defining how you calculate locations within the structure and how big each element is. You define code, not memory patterns. That's very, very different than other languages. If I had to go out on a limb, I'd say this reminds me more of assembly
Starting point is 00:44:21 than anything else. When I write assembly, I tend to write either macros or small helper functions to retrieve or set data. If I'm working with, say, a list, I'll write a function to get and set list elements. That's vaguely similar to the approach Bliss takes, but not quite the same. Bliss lets you actually bind the structure, those access expressions, to variables,
Starting point is 00:44:47 so the language treats those variables in a special way. Now I think that should do it for the entire data part of Bliss, and let me be clear here, the data part is really the strange part of the language. Once we get to things like loops and scope and conditionals, stuff settles down quite a bit. A big reason for this is that Bliss is based off Algol. Many languages, including C, are related to Algol at some juncture. It's where we get a lot of modern rules for things like block structure and scope. That's why a lot of languages look vaguely similar. Bliss is no exception. It uses blocks of code marked by beginning and end exactly like an algal. Lines end in semicolons, just like an algal. So the general shape of the language is somewhat familiar to the programmer. By 1970, the first version of Bliss was ready for use. Now, the matter of
Starting point is 00:45:50 platform here is important. The first Bliss compiler was developed at Carnegie Mellon for the PDP-10, made by DEC. In the next few years, it would become a somewhat cross-platform language, as long as you were on a DEC computer. In fact, Bliss would become what seems like DEC's favorite language. For this part of the story, I'm pulling from Roland Brinder's paper, The Bliss Programming Language, A History. This is a good paper, but do note if you seek it out that Brenda writes in the third person in some spots. That, um, that tripped me up for a while,
Starting point is 00:46:31 because Brenda himself was the one that brought Bliss to DEC. It all starts in 1970, when Brenda met Wolf and was introduced to this strange new language. By that time, Bliss was already running on Deck hardware. There was also active work to adapt the language to a new machine, the CEMMP. This is a whole topic in and of itself, so let me just touch on it and move on. The CEMMP was a research machine under development at Carnegie Mellon. It was one of the first big multi-processor systems and its operating system, called Hydra, was written in Bliss. Bliss was already showing
Starting point is 00:47:12 some promise at CMU. Brenda explains in his history that at that point, the language had been used for a number of utilities on the PDP-10, and crucially, a compiler. This was supposedly a quote-unquote what-for-like compiler. I know, vagaries upon vagaries. This takes a second to unravel, but it's worth it. At this point, Bliss is self-hosting, meaning that its compiler was actually written in Bliss itself. But remember, Bliss is a bit of a weird language. It has a pile of idiosyncrasies, and its compiler is somewhat unique and complicated. So, what is Watt4 and how does it factor in? Simply put, it was a third-party Fortran compiler written at the University of Waterloo.
Starting point is 00:48:04 Now, I'm going to make an assumption here. It was a third-party Fortran compiler written at the University of Waterloo. Now, I'm going to make an assumption here. When Brinder says WOT4 like, I'm assuming he means that this mystery compiler handled Fortran code. This is a little tenuous because what made WOT4 special is that it pulled a few tricks so you could immediately execute a Fortran program after compilation. It could be that Render meant someone wrote a similar tricky compiler for some other language, but I'm guessing it was for Fortran because that makes the next step more logical. Either way, by 1970 there
Starting point is 00:48:41 are two compilers written in Bliss. One of those is for some non-Bliss language. Render learns about the language, gets a demo or two, and becomes a convert. To quote, As the Bliss compiler stabilized, Render turned his attention towards introducing its use at deck. Fortunately, a new optimizing Fortran compiler development project, known as Fortran 10, was starting at a good time to use Bliss 10. As compiler and language-oriented people themselves, the development team was eager to use a high-level language for their work, as well as being perhaps less risk-averse than others regarding the strange new language from CMU. The Fortran 10 group plunged into using Bliss and never looked back."
Starting point is 00:49:30 A note on notation here. It was common to name DEC software after the version of PDP computer it ran on, so Fortran 10 was for the PDP 10, as was Bliss 10. Once Bliss moved into DEC, it really became cemented. According to Brinder, there was some resistance in the larger community of DEC users and employees. He says it came to a head at a DEC user group conference, DECUS, when attendees started wearing buttons that said, Bliss is ignorance. That sounds like a great story, and apparently it's true. I can't find images of said pins, but there are contemporary accounts of this slogan showing
Starting point is 00:50:14 up at user group meetings. This animosity was eventually tamed once Fortran 10 came out. It proved to be a capable compiler, which made a lot of detractors retract their position. Future DEC projects would rely heavily on Bliss all the way up into the VMS era. So then, why was there so much pushback? That's one of those things that's hard to quantify. Period arguments get muddied by later speculation. In 1971, as Bliss was making its debut at DEC, C didn't exist. There wasn't a clear frontrunner in the field. In fact, there were very few options when it came to high-level system programming languages. As
Starting point is 00:50:59 mentioned way at the top, you had a dialect of Algol, PL1, and maybe BCPL if you were in England. The most basic reason for resistance comes down to the classic argument. None of these languages can be as powerful as assembly. This is the same argument that John Backus faced way back when he started developing Fortran. In that case, programmers didn't want to try a high level language because of concerns over performance and precision.
Starting point is 00:51:28 If you write an assembly or a machine code, you have full control over everything the machine does. With a compiled language, the compiler decides what the machine does. You have to give up a little control, which is hard to do. By 1970, the performance argument had been blown out the water. A generation of programmers had been raised with high-level languages by that point. But the precision part of the argument, that was still leaned on specifically in systems
Starting point is 00:51:58 programming. That kind of code does need a high level of precision after all. So there are some valid reasons to argue. The main critiques I've seen about Bliss come from after C is introduced. I found quite a few interesting threads from Usenet that discuss issues with Bliss in the 80s, and a few mentions in some papers and mailing lists. One strangely period argument that I did run into actually comes down to go-to. Remember how that was a core design goal? Bliss was initially envisioned as a language without any form of go-to, something to make Dykstra proud. According to some, that was handled poorly. Listen to this from 1975 in a paper titled Structured Programming Considered Harmful.
Starting point is 00:52:46 Quote, It is interesting to see what happened in Bliss, a language whose designers eschewed the go-to because, in their view, it was redundant. They were forced, as a consequence, to introduce seven new statements. End quote. The paper goes on to list the seven different forms of exit statements used in Bliss. And this is actually true. In Bliss, there are seven special expressions for exiting blocks, loops, conditional statements,
Starting point is 00:53:18 functions, and a few other constructs. It's one of those weird quirks of the language. You can't just go to some location outside of your current block. You have to exit. But since Bliss supports nested blocks, an exit can, in some cases, be ambiguous. Do you want to exit from the loop you're in, or from the case statement inside of it? As a result, you get a weird set of specialized exit calls. But that's more nitpicky than anything. The vast majority of arguments against Bliss come down to its data handling. This even comes from within CMU itself. In 1972, Wolf co-authors
Starting point is 00:54:01 a paper called Reflections on a System Programming Language, which discusses lessons learned a few years into using Bliss. One of the critiques is the danger of pointers, especially as implemented in Bliss. This was a known issue by the 1970s. Pointers are very powerful, but they introduce whole classes of possible bugs. If there's nothing to check where a pointer is pointing, then you could end up pointing back at yourself. In some languages, that could be avoided, just have the compiler throw an error if you
Starting point is 00:54:36 point into a dangerous direction. But Bliss lets you do all kinds of weird runtime tricks with pointers. You can make a pointer on the fly. What happens when pointer math, which can be very complex in Blis, goes wrong? Well, we already knew the consequences. Folk were doing similar tricks with pointers in assembly language. Blis made the tricks more sophisticated, but the same type of pointer math was common on the lower levels. When you made a mistake, you'd get a particularly nasty and hard to spot buck.
Starting point is 00:55:12 I know, because I've made those kinds of mistakes. Most recently, I wrote some bad code that accidentally pointed to a return stack. It took me at least a day to figure out what was wrong. Bliss's pointers are very powerful, but they can also be very unstable. There's also the matter of type. This is one of the largest arguments against Bliss in more modern discourse. Typing makes a language safer. What do I mean by that? By enforcing data types, you prevent a lot of accidental and just stupid bugs. In a more strongly typed language, you can specify that a function must be given a number, for instance.
Starting point is 00:55:55 That prevents you from, say, trying to add a number and a character and getting unexpected results. Or perhaps trying to do some pointer math and accidentally adding two irrelevant values, resulting in some nonsense pointer directed out into memory space. There's one other big issue that comes up in any critique of Bliss. That is, portability. One early design goal was machine independence. Now in theory, Bliss as a language could have been portable. There's nothing very machine dependent about the language
Starting point is 00:56:32 as such, or so it would seem. There are some specific details about its data type, the full word, that cause issues. Bliss 10, the first compiler, used a 36-bit word. That meant code written for Bliss 10 assumed that each variable would be 36 bits wide. The next compiler, Bliss 11, was written for the PDP 11. That machine had a 16-bit word. So a full word in Bliss 11 is 16 bits instead of 36. There were also changes in how pointers were handled because the PDP 11 handled addresses differently than the PDP 10. The result is that code written for Bliss 11 isn't necessarily compatible with
Starting point is 00:57:17 Bliss 10 and vice versa. Eventually, DEC codified a so-called common bliss, a standardized dialect that was fully portable. But that didn't happen until the latter part of the 1970s. And it was only really used within DEC. The language was kind of trapped. Then we get to the real elephant in the room. In 1972, a language called C is developed inside Bell Labs. In 1973, Unix is rewritten in that language. In 1976, Lion's commentary on Unix is published, which
Starting point is 00:57:54 is used extensively in university classrooms. The book uses Unix's source code as a case study in programming, carrying C along with it. I personally think that once the commentaries are published, it's a little too late for Bliss. UNIX spreads quickly after that point, as does C. The spread of the language is, of course, much more complicated than that, but I think that's a reasonable summary. The C programming language was developed with many of the same design goals as Bliss. It was meant to be a systems programming language. It was designed specifically to be used in Unix, an operating system. C has pointers, but they're slightly safer than the full throttle pointers used by Bliss. Both languages are inspired by algal,
Starting point is 00:58:46 so the syntax is even similar. The big difference, what nearly every argument boils down to, is typing. C has variable types. Bliss does not. That means that C is, right off the bat, a safer language. At least somewhat.
Starting point is 00:59:04 It's still kinda dangerous with how it handles pointers, but, you know, potato potato. On a technical level, C is less error-prone than Bliss, but it's able to do most of the same things. On its own, that doesn't necessarily lead to a winning language. The final factor is availability. The spread of Unix is tied to the spread of C. You can't get one without the other. As more and more mini computers start using Unix, that means more and more users have access to C. The C compiler comes along with every version of Unix.
Starting point is 00:59:39 And since Unix was initially distributed as source code, every Unix installation came with a pile of C code to look at. By entering Unix, you were entering the world of C. Bliss, on the other hand, never had that kind of availability. DEC used a lot of Bliss, but the Bliss compiler was never generally available to the outside world in the same way as the C compiler. That paired with its shortcomings made it a much weaker showing than C. I just don't think there is ever any getting around that.
Starting point is 01:00:15 Alright, that does it for today's episode. We've seen how Bliss emerged, what made it unique, and what made C better? Does this mean that Bliss is a bad language? Is Bliss truly ignorance? I'm tempted to say yes, but I've learned better. My saga with track has taught me to judge languages a little less harshly. Bliss is a very unique language, especially for a high level language. It represents the snapshot in time when we didn't have very good tools for high-level system programming. Sure, there are parts of its design that are flaky,
Starting point is 01:00:52 dangerous, and perhaps annoying. However, there are reasons for those choices. It's not just an amateurish system, it's not just something to point at. It was built to solve a very specific problem, and good choices were made for a lot of its design. C isn't just a better language than Bliss, it's not that simple. There are many features of C that are better than equivalent features in Bliss. Take the entire data handling parts of these languages. The complication here is that Bliss may be better suited for representing some types of code. You can just solve problems in different ways in Bliss because its variable system is so
Starting point is 01:01:32 different. I'm also saying this because Bliss has kind of charmed me. It's an interesting language that I'd actually like to mess around with. To that end, there are modern compilers. Notably, OpenVMS comes with a Bliss compiler, which you can actually get hobbyist license for now. That program was cancelled for a while, but it's back, so if you want to play with OpenVMS as a hobbyist, you can do that for free. FreeVMS also has a Bliss compiler, from what I've read. There's also the Bliss-im compiler, which is an open source project.
Starting point is 01:02:06 I've been playing around with that one myself. You can compile Bliss M on Linux, so it's easy enough to use. Right now I'm trying to link some Bliss to an entry point written in C, so I can try to boot a computer into Bliss. Gonna see if that actually goes anywhere, maybe a fun weekend.
Starting point is 01:02:25 The point is, if you want to take the plunge, there are tools and resources out there. Thank you so much for listening to Admin of Computing. I'll be back in two weeks' time with another piece of Computing's past. If you like the show, there are a few ways you can support it. You can tell your friends. Word of Mouth is a great way to help grow the show show You can also rate and review the show on Apple podcasts and Spotify You can support the show directly by signing up as a patron on patreon or buying merch from my merch store Links to all of that are on my website admin of computing comm
Starting point is 01:02:59 Right now on the patreon as I mentioned in the top. I'm in the process of picking the next bonus episode That process is going to go on for a few more weeks, so if you want to get in on that, please go over to my website, go to the Patreon and sign up. It costs as little as a dollar a month. And as always, have a great rest of your day.

There aren't comments yet for this episode. Click on any sentence in the transcript to leave a comment.