22 ms·
Features of PL/I not realized in a modern language
- manbart 5y agoI think 2,3,5 and 7 apply to Rexx as well, another IBM created language. I wouldn’t call it modern however
- npsimons 5y ago> I wouldn’t call it modern however I wonder how the claim stands up if we remove that "modern" adjective. Or perhaps consider that "modern" languages don't have some of these features as they turned out to be "harmful" (see Dijkstra's "GOTO considered harmful") - at least one reply in that thread calls out one of these "missing features" as very dangerous, sort of like how some C++ projects will forbid features (private inheritance, etc) for ease of maintenance.
- bigbillheck 5y ago> 7. Astonishingly complete set of implicit data conversions. E.g. if X is floating-point and S is a string, the assignment X = S works when S = "2" and raises an exception (not PL/I terminology) when S = "A". This seems like an extraordinarily bad idea.
- jrd259 5y agoPerhaps a bad idea, but at least it was rigorously specified. I note that that Javascript and perl have same ability. See also https://www.destroyallsoftware.com/talks/wat https://www.destroyallsoftware.com/talks/wat
- layer8 5y agoJavascript and Perl are different in that they allow a variable to change it’s type by reassignment (i.e. they would allow the X = S assignment for S == "A"), which PL/1 apparently doesn’t. Weak/strong typing and dynamic/static typing are different axes.
- masklinn 5y ago> Javascript and Perl are different in that they allow a variable to change it’s type by reassignment That is orthogonal to the issue at hand. > Weak/strong typing and dynamic/static typing are different axes. And perl and javascript have both, and the weak typing is what gets criticised the most, and what causes the most issues.
- layer8 5y agoYes, the weak typing is what (rightfully) gets criticized, but the PL/1 design here is IMO closer to C with its implicit conversions but typed variables than to Perl and Javascript.
- masklinn 5y ago> the PL/1 design here is IMO closer to C with its implicit conversions but typed variables than to Perl and Javascript. So in your opinion, the PL/1 design here is closer to a trainwreck than an other trainwreck? Furthermore C's implicit conversions come nowhere near parsing a string to get a float, that's squarely on the Perl/JS side of the board.
- layer8 5y agoLook, I wasn’t expressing any value judgment, and I’m not advocating for implicit conversions. My point was that the implicit conversions in PL/1 have a different context in PL/1 than they have in Perl and Javascript, more akin to the one in C. One effect of that is that the type of a variable remains fixed in PL/1 where it doesn’t in Perl and Javascript. This places it at a different point in design space and changes the dynamics to some extent, which is just a nuance that I find interesting. It wasn’t expressed in the original comment I was replying to, so I thought it would be worth pointing it out so that he distinction isn’t being missed.
- masklinn 5y ago> I note that that Javascript and perl have same ability. And it’s widely considered horrible.
- drfuchs 5y agoAnd all the implicit conversion rules between pairs of types couldn’t fit on a single-page chart; the official IBM manual included an honest-to-goodness fold-out bound right in. Fun times.
- ptx 5y agoIt does. It sounds like the Unicode handling in Python 2, where your program would still appear to work if it confused bytes with codepoints (through implicit decoding/encoding), until you get a non-ASCII input somewhere and everything blows up with a UnicodeDecodeError somewhere far from where the problem is.
- layer8 5y agoIt’s like implicit numeric conversions in C, just extended to string types.
- zokier 5y agoIts not like the implicit numeric conversions in C were particularly good idea either
- emmelaich 5y agoIt was a mistake in C++ too, fixed with the explicit keyword.
- masklinn 5y agoIt’s not fixed since it still occurs by default. That you can work around it if you remember to is not a fix.
- layer8 5y agoIndeed. My point was that it’s something also found in other widely used languages, and not a PL/1 pecularity.
- masklinn 5y ago> My point was that it’s something also found in other widely used language where it is also generally considered > an extraordinarily bad idea. so it seems like a rather useless point?
- teddyh 5y agoPoint 7 simply means that PL/I is very weakly typed. However, weak typing seems to now be unused in all modern languages; strong typing seems to be the current norm. (Not to be confused with static/dynamic typing, which is orthogonal.)
- throwawayboise 5y agoUsed PL/1 in one of my first programming classes at university. Never before or since, but I don't recall it being a bad language to work in.
- jrd259 5y agoPL/1 was the systems programming language for Multics. Certainly not a bad language to work with.
- pjmlp 5y agoAnd plenty of other systems as well.
- reidacdc 5y agoPL/I was the software-engineering language in my undergraduate computer science classes also, although it was beginning to be displaced by Pascal in the introductory courses, and C/C++ in the advanced courses. (Mid-1980s, in case that is not already clear.) It hung on because, we were told, it was still in demand in industry, particularly at telephone companies. I did a group project with it, and the language was large enough that we found that each member of the group seemed to have learned a slightly different subset of it.
- throwawayboise 5y agoSimilar timeframe. Used PL/1, Pascal, and Modula-2 in three successive semesters IIRC.
- adrian_b 5y agoThere are many other features of PL/I besides those mentioned there, which are not encountered in modern languages. Because PL/I was designed by IBM for their computers and operating systems, there were no concerns for portability, so the language definition included a large amount of features that are left unspecified in standards for languages like C/C++, because they are available only for certain operating systems or computers, so the standard must be restricted to specify only the minimum features that are available everywhere. For example the C++ threads are limited only to the features provided by the POSIX pthreads, even if most operating systems have additional features for multi-threading. PL/I on the other hand, already in 1965 had more powerful features than the C++ threads. One feature of PL/I that is completely absent in all modern languages is what I consider to be the right way for error handling. Instead of being forced to test the return value of a function to determine what error might have happened, which is a redundant operation, as this was already tested once in the invoked function, and which clutters the code, obscuring the processing of the normal path, the PL/I method was to group the error handlers at the end and pass the labels of the error handlers to the invoked function as alternative return labels. While this looks visually very similar to a try block with exception handlers, the hardware implementation in PL/I is far more efficient than in modern languages, because there are no conditional branches and no extra code to determine where is the appropriate error handler, just the return, which is done anyway when the invoked function finishes, jumps to different addresses in case of errors, instead of returning to the point of invocation. PL/I also had exceptions, but those were used for really exceptional conditions, like numeric overflow, illegal memory accesses etc., not for correctable error conditions, like a mistyped user input or a file that cannot be found, which are best handled at the point where a function has been invoked, because only there you have complete information about the intention of the failed operation.
- p_l 5y agoPL/I model was inspiration for Common Lisp condition system, which handles both recoverable (continuable) and non-recoverable conditions (akin to exceptions). Continuable exceptions are also a thing in Windows NT, though rarely seen in practice. All of those hark back to PL/I error handling, possibly through Project MAC (which was probably bigger PL/I user than anything from IBM for a time)
- npsimons 5y agoUpvoted simply out of controversy and curiosity. It's been a long time, and I've not delved deeply in to a wide swath of languages (recently), but surely the features he's citing are in other languages? Hell, even the list of "features only Lisp has" keeps shrinking by the decade. Some of these PL/I "features" feel like things you could trivially implement in a library in other languages (ie, not have to implement half of PL/I to get those features). Some of it sounds simply like syntactic sugar, and not actual features. What am I missing here?
- ptx 5y ago> 4. A named array is normally passed by reference, as in F(A). But if the argument is not a bare name, as in F((A)), it is passed by value. Visual Basic (VB6/VBA) does exactly this, with the same syntax (except without the outer parentheses if the call is a statement).
- kergonath 5y agoFortran does it as well, I believe. The reason is that (A) is an expression whose value is that of A, and not a variable. This includes Fortran 2018; I am not arguing whether this is modern or not.
- throwawayboise 5y agoIt seems quite reasonable and intuitive to me.
- gnufx 5y agoFortran actually passes by value-return, or whatever you call it, not by reference. The call may still be compiled by reference, of course, and it used to be fun to change the value of a number in the days when it wasn't in read-only storage.
- bregma 5y ago"Fun" in the Dwarf Fortress sense. We once spent days trying to track down an obscure malfunction only to discover that someone had passed a 0 literal as an actual parameter to a certain function that assigned 4 to the formal parameter. For those following along at home, that meant the value of any 0 literal in the program suddenly became 4.
- gnufx 5y agoI don't know what Dwarf Fortress is, but that's what I meant. Fortran now has INTENT specifiers, and you can expect constants to live in .rodata(?) on ELF systems, so you get a SEGV if you evade any compiler warning.
- the_only_law 5y ago> Bit strings of arbitrary length, with bitwise Boolean operations plus substr and catenation This one actually piqued my interest in PL/I recently. Closest thing I’ve seen in modern languages is bitstrings in the BEAM.
- throwawayboise 5y agoYes I also thought of Erlang bitstrings when I read that.
- p_l 5y agoI believe both Ada and Common Lisp also share this ability, though Ada has nice support in its type system for doing things like mapping a portion of mmio register as bitstring that gets further defined as new type (whether enum, or integer, or string etc.)
- the_only_law 5y agoIf Ada has something like this, I haven’t seen it yet, but I’d be pleasantly surprised to learn otherwise. I’ve heard about lisps having bitstrings support, but it was only briefly explained to me years ago and I haven’t seen it since.
- Someone 5y agoMost modern languages have the concept of “libraries” that one can “import” and that can extend the language’s feature set. For example, I don’t think C++ has such bit strings (the confusingly named bitset (https://en.cppreference.com/w/cpp/utility/bitset https://en.cppreference.com/w/cpp/utility/bitset) comes close, but don’t think it does the substr and concatenation parts. basic_string<bool> has those, but not the Boolean operations), but they can be added when needed.
- moonchild 5y agoAPL usually uses bitstrings, though that is an implementation detail. Then, I guess APL is not exactly 'modern' anymore...
- Someone 5y agoAnother feature: keywords aren’t reserved words. For an example of where that can lead to see https://multicians.org/proc-proc.html https://multicians.org/proc-proc.html The thinking behind that was that the set of keywords would grow over time, and that you couldn’t expect any programmer to know all of them. There likely also are few programmers who do know all of them, as there are hundreds (see https://www.kednos.com/pli/docs/reference_manual/6291pro_042.html#append_key https://www.kednos.com/pli/docs/reference_manual/6291pro_042... for those of one implementation)
- curtisf 5y agoJava has been using this approach with new reserved identifiers like `var` and `record` in the interest of backwards compatibility. JavaScript has done something similar with its `await`, `let`, etc
- colejohnson66 5y ago> Another feature: keywords aren’t reserved words. This leads to this confusing block of code being legal (the syntax is almost certainly wrong however): IF IF = THEN THEN THEN := ELSE ELSE ELSE = THEN THEN THEN := ENDIF ENDIF
- jdougan 5y agoMy personal favorite feature in PL/1 was the zero overhead matrix transpose. You achieved this by mapping two matrix declarations onto the same storage, one specifying column-major and the other row-major. We used to joke the PL/1 declaration system was turing-complete.
- moonchild 5y agoThe interesting use-case for transposition is not just switching around indices, but changing layout so a given access pattern exhibits greater locality. No getting around the overhead there :/
- tofflos 5y ago> 1. Assignment by name. If A and B are structs (not official PL/I terminology), then A + B A = B copies similarly named fields of B to corresponding fields in A. Do you mean something like with-expressions? See https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/with-expression https://docs.microsoft.com/en-us/dotnet/csharp/language-refe....
- steeleduncan 5y agoI believe golang has a feature like this as well
- masklinn 5y agoThe text isn't really clear, but from the wording it reads like the PL/I version will copy between existing instances of completely unrelated types. AFAIK `with` will only "copy with overwrites", similar to e.g. Rust's `..x`.
- musicale 5y agoPL/I was also memory safe (unlike C/C++), and on Multics the stack grew up rather than down. Probably the greatest reliability regression from Multics to Unix was implementing the kernel and user apps in an unsafe language. (Or, more accurately, using an unsafe compiler/ABI, as there have been memory-safe compilers/runtimes for ANSI C such as Fail-Safe C.)
- jhallenworld 5y agoI think the more relevant language was PL/M (actually PL/M-86) since it was available for 8086 early on. I don't think PL/M had any of these features, and C for the 8086 was better than it so that was the end of it. Maybe if IBM chose 68K for the PC, we would have gotten the full blown PL/I. Edit, well actually I found this "X3.74, PL/I General Purpose Subset (Subset G)" from 1983. This version at least has strings. Look at manual disk here: https://winworldpc.com/product/digital-research-pl-i-compiler/1x https://winworldpc.com/product/digital-research-pl-i-compile...
- BXLE_1-1-BitIs1 5y agoPL/I was an order of magnitude step from Fortran and Cobol. The earlier languages made direct calls to the operating system while PL/I used subroutine librairies to do the operating system heavy lifting. The library routines in many cases would produce messages of various utility (usually with the number of the failing statement) instead of causing the application to terminate aka ABEND. Yes, you can use bit strings in PL/I, but the subroutine overhead was ginormous. This is simply the worst case of PL/I data conversion routines. PUT EDIT and GET EDIT are somewhat less costly. Structures are a two edged sword. Best to have them STATIC in the owning routine and have them based on pointers for other routines. Otherwise the prolog code can dwarf the code that does useful work. PICTURE works very well in structures. Assigning between structures with fields of the same name in one PICTURE, and the other in machine format can often be performed without resort to data conversion routines. Bottom line: Many advertised features are costly while PL/I offers remarkably efficient alternatives.
- kgeist 5y ago>If A and B are structs (not official PL/I terminology), then A + B A = B copies similarly named fields of B to corresponding fields in A. In Go, you can copy a variable of type A to a variable of type B if the types have similarly named fields of same types, although an explicit cast is required.
- denton-scratch 5y agoI used PL/M at one time - PL/M for microprocessors. It was an Intel compiler. PL/M was a subset of PL/1, I think with 64Kb object segments (which suited 8086 architecture). It was a nice, predictable systems programming language.
- hungryforcodes 5y agoIt's not really mentioned in the thread so far, but weirdly... PL/I was one of the inspirations for PL/M, which was the basis for CP/M... which was the basis for DOS... which led us to Windows... which leads us to today for those that don't use other OSs, etc, etc.
- timbit42 5y agoPL/I and PL/M are languages. CP/M is an operating system. The '/M' and that CP/M was mostly written in PL/M are the only 'basis' for CP/M from PL/M, but PL/M didn't affect what CP/M was created as.
- hungryforcodes 5y agoWait. You're saying that CP/M was created with PL/M but PL/M didn't affect CP/M?! It's been a long day huh?
- timbit42 5y agoI'm saying PL/M didn't affect the design of CP/M. You could write CP/M in C or assembly and it would be the same.
- hungryforcodes 5y agoOK, I see what you mean. I think actually the BIOS was written in assembly, and you had to customize it for each system you made CP/M for -- so fair point. :)
- mgraczyk 5y agoGenerally in modern languages you use libraries rather than language features, which IMO is better and shows that the language is generic enough to allow programmers to build abstractions as needed. However, some of these (#4 in particular) are exactly supported in other "modern" languages. 1. Assignment by name. python: A = {**A, **B} javascript: A = {...A, ...B}; 2. Both binary and decimal data For these you use libraries like Decimal in python, boost::multiprecision in C++ 3. Bit strings of arbitrary length vector<bool>, boost::dynamic_bitset, or write your own in python you would use numpy 4. A named array is normally passed by reference, as in F(A) In C++ you can do this for any type with a copy constructor. void f(A_Type& A) { ... ... = f(A); // by ref ... = f({A}); // by value 5. IO by name. Not generally done as far as I'm aware, but you could easily build this in C++. Something like class StdOut { void operator=(string s) { cout << s; } }; StdOut{} = "hello\n"; 6. SORT statement Why not sort function? c++ std::sort, python sorted() builtin 7. Complete implicit conversion Javascript is the KING here, and we all know how helpful that is.... Of course C++ let's you define this yourself, but there isn't directly language support.
- HelloNurse 5y agoI don't think the C++ aggressive implicit conversions are particularly well liked. Saving a little verbosity by omitting an explicit conversion is rarely worth the risk of silently derailing overload resolution; it was probably already a bad trade off in PL/I.
- npsimons 5y ago> Generally in modern languages you use libraries rather than language features This was exactly the feeling I got as I read his list: "these aren't features, they are, at best, nice to have library functions and syntactic sugar."
- stackedinserter 5y agoTo me it looks like a list of ways to shoot yourself in the foot or get a pagerduty call at 3am. E.g. what's good about automatic conversion between float and str? It can save you a minute or two if you're making a PoC or fiddling around in a notebook, but it's hiding real issues in your data flow if you're developing something for production use.