Hacker News new | past | comments | ask | show | jobs | submit login

> However if you were creating UNIX today you'd definitely want to look at OOP and inheritance as a core pillar.

I doubt it. Imposing structure and types on IPC would make it very difficult to compose separate programs.

Suppose I created UNIX-2, where the only difference between UNIX and UNIX-2 in principle is the fact that UNIX-2 programs all pipe serialized Objects to and from each other, instead of byte streams.

Now, the ls program obviously outputs more information than just a list of strings--it also outputs types of files, permissions, owners, inode numbers, sizes, timestamps, etc. I might be inclined to have ls output an lsOutputObject (derived from Object) that encapsulated all this information.

Suppose I wanted to pipe the output of ls into wc. How does wc handle an lsOutputObject? Either wc is programmed to know how to handle lsOutputObject, or it is not. Since we want an object-oriented environment, we'll assume the former case, so wc can call the appropriate object-specific methods and access the appropriate object-specific fields. But, now wc is tightly coupled to ls.

This problem generalizes. For a given program P in a set of N programs, wc will need to know how to access P's output-object-specific fields and methods. So, wc needs O(N) different subroutines to interact with the N other programs. This does not scale--each additional program I add to UNIX-2 will require me to write O(N) additional IPC handlers--one for each program.

The only way to avoid this IPC-handler-explosion in the design is to define the set of IPC objects a priori and mandate all programs know how to handle them. Then, there are O(1) IPC handlers per program, and adding a new program does not require me to couple its implementation to any other programs. This is effectively what UNIX does: there is one IPC object--a string of bytes. In UNIX-2 I could have more types of objects, but the fact that they're defined independent of the programs means that I will still be "hoping the receiving process understands" when I give it data from arbitrary programs.

Suppose I relax this a priori object mandate above to allow programs to extend the base IPC object types. But then, programs that do so will only compose with programs that implement IPC handlers for their extended object types. In this scenario, I can expect there to be disjoint sets of programs that compose with one another, but not with others. This is effectively what happens in SOA/microservice architectures: you get a set of programs that are composible only with programs that speak their (arbitrarily-structured) messages (the set of which is much smaller than the global set of SOA/microservice programs).

My point is, trying to enforce OOP on IPC will take away universal program composibility, which is the killer (if not defining) feature of UNIX.




But there is already a de facto data serialization in Unix: tabs/spaces as field separators and newlines as record separators. That's what allows ls and wc to interoperate, but it makes it such that ls and cut don't interoperate without a transformation in between, because of ls's use of columnar layout rather than separator based layout.

Or the output of a 'uniq -c', you can then 'sort -g' to order lines by the number of occurrence, but if you want to take the top 5 lines and discard the counts with 'cut', you need whitespace transformations in between. (AWK would be an alternative to cut that performs the whitespace conversions on its own, but AWK is a full blown programming language, so one may as well have something that deals with dictionaries, arrays, etc, as input types anyway).

All this is to say that the untyped bytestream relies on conventions in Unix to make the bytestream useable between composable programs. These conventions are adequate for current uses, but show some weaknesses, and suggest that perhaps there are additional conventions, that if sufficiently simple, could be used to build composable programs that don't need to understand a format that is particular to just one program.

I totally agree that objects (meaning data + associated data-specific code) are probably overkill, though optional object interfaces may be nice if the programmer is willing to pay the computational cost, like one does when using AWK over cut. And I definitely think that inheritance is an idea best to avoid for data.


> But there is already a de facto data serialization in Unix

De facto is a far cry from "required by the OS". You'll notice that the programs we both mentioned (as well as programs that interpret whitespace this way) tend to operate on human-readable text, which uses whitespace to separate words. However, there are many other programs that do not operate on human-readable text, and do not rely on whitespace to delimit records in a pipe (or other file-like construct). Thus, the OS should not try to enforce a One True Record Delimiter, since there isn't one.

> ...and suggest that perhaps there are additional conventions, that if sufficiently simple, could be used to build composable programs that don't need to understand a format that is particular to just one program.

This really is the heart of the matter. There is a trade-off between the specificity of the conventions and the freedom of the program to interpret the data however it wants. UNIX is at (almost) the extreme right end of this spectrum--the only convention it imposes is that information must be represented as 8-bit bytes.

My question to those who feel that UNIX is too far to the right on this spectrum is, what are some conventions that can be adopted universally that won't break composability? I'm not convinced that there are any. Even simple things like requiring programs to communicate via a set of untyped key/value pairs (where each key and value is a string of bytes) would be risky, since it could easily lead to the creation of disjoint sets of programs which only work with members of their own sets (e.g. members of each set would require set-specific key/value pairs).


The thing is we're already dealing with disjoint sets of programs and broken composability. Thats why so many shell one liners take the form

  produce-data | munge | invoke-seperate-programming-language | munge | do-something-with-data
I mean we have to do the C->Bash->Awk->Bash->C precisely because nothing actually works together and thats with just text. If we did mandate some IPC and ran across a program that couldn't speak it? Well as long as our smart program had a toString equivalent we wouldn't be any worse off than we are now. But between programs that spoke it we'd be better off.


> The thing is we're already dealing with disjoint sets of programs and broken composability. Thats why so many shell one liners take the form...I mean we have to do the C->Bash->Awk->Bash->C precisely because nothing actually works together and thats with just text.

You seem to contradict yourself :) Yes, you might have to munge some text to get the disparate programs to work together. But, at the end of the day, you get the result you want, because munging the data is possible. Contrast this to a world where munging the data is all but impossible, since each program speaks different objects. Then you'll have some real breakage on your hands, unless you can write O(N^2) inter-object mungers to replace the O(1) text-based mungers you were using before.

> If we did mandate some IPC and ran across a program that couldn't speak it? Well as long as our smart program had a toString equivalent we wouldn't be any worse off than we are now. But between programs that spoke it we'd be better off.

How would this be an improvement? Most programs (i.e. the ones that don't know your "smart" program's IPC object format) would fall back to using the toString() method. The comparatively small set of programs that can use your program's IPC objects would be tightly coupled to each other, meaning a change to the IPC object structure or semantics will require modifications to most/all of the programs. If anything, your proposal exacerbates the "disjoint sets of programs" problem, and has the effect of turning the legacy/compatibility option (toString()) into the ironically future-proof method for interacting with "smart" programs.


Oh, I totally agree that the byte stream should be only stream that the kernel itself is concerned with. I'm merely arguing that there's room, in addition to the current situation, for having more highly structured data streams between Unix CLI utilities. I've had great success with piping binary formats for very specialized use, both for IPC and across the network, and I'd like to see additions to the Unix toolset that take advantage of some of those greater capabilities, in a language- and OS-indpendent way.


> I'd like to see additions to the Unix toolset that take advantage of some of those greater capabilities, in a language- and OS-indpendent way.

Don't get me wrong, I won't turn down structured IPC just because it's structured :) However, I have yet to see an IPC system that (1) gives you more than a byte-stream, and (2) allows for universal composability. Every IPC system I have ever come across sacrifices one of these properties to make gains in the other. While I haven't ruled it out, I'm skeptical (but would love to be proven wrong) that there exists a form of IPC that can meet both requirements.


Dbus.

In the very near future, everything on Linux will be done with dbus.


I sincerely hope you are joking. Dbus-as-IPC doesn't make for composability at all, since each program must be aware of (1) the names and signatures of each other program's dbus-accessible methods, and (2) each other program's behavioral semantics with respect to the method invocation.


> The only way to avoid this IPC-handler-explosion in the design is to define the set of IPC objects a priori and mandate all programs know how to handle them. Then, there are O(1) IPC handlers per program, and adding a new program does not require me to couple its implementation to any other programs. This is effectively what UNIX does: there is one IPC object--a string of bytes.In UNIX-2 I could have more types of objects, but the fact that they're defined independent of the programs means that I will still be "hoping the receiving process understands" when I give it data from arbitrary programs...My point is, trying to enforce OOP on IPC will take away universal program composibility, which is the killer (if not defining) feature of UNIX.

Not necessarily. You could pass S-expressions instead of bytes. Parsing would be much easier, and there would be no loss of generality.


Either wc needs to specifically be aware of the structure of the S-expression ls emits in order to know which atom or subtree of atoms make up words and lines, or ls must emit an S-expression with a structure that is globally mandated a priori. The former case loses generality (wc must be aware of ls's behavior), and the latter case gives wc no help in interpreting the S-expression (which is the original problem OP pointed out).




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: