There are many other features of PL/I besides those mentioned there, which are not encountered in modern languages.<p>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.<p>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.<p>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.<p>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.<p>While this looks visually very similar to a <i>try</i> 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.<p>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.