Oh, I would like to give an extensive answer to this to do Oberon fair justice. So, sorry if this is short and simple. Also, do not hesitate to tell me where I could have been wrong - I think I know Oberon fairly well, but obviously not nearly as well as you do. So I'll just give a few points off the top of my head:
- Keywords are all uppercase, and the language is case-sensitive. Uppercase keywords really annoy me and my fingers. They were justified back in the days when text editors didn't have syntax highlighting, but these days, they are just a major annoyance IMHO.
This would have to be the most common gripe I have heard. To me it is like somebody saying they wouldn't drive a Ferrari because they can't get it in any colour other than red.
My typing isn't particularly good either and it was one of the major incentives for me to develop the Oberon-aware Astrobe IDE. One of its features is automatic capitalisation of keywords. You just continue typing in lowercase as normal and when it recognises the last word typed is a keyword it capitalises it for you. We've also catered for the rare occasions where you don't want this to happen and find it works extremely well in practice.
Actually it would have been easier to have modified the compiler to accept lowercase keywords as well. There are several reasons why I believe a case-sensitive language and uppercase keywords can be useful:
a) As long as you avoid all-uppercase identifiers you know you can use whatever word is the best choice for a particular variable.
e.g. some of the following would be invalid in Pascal:
colour := black; type := digital; case := plastic;
now := 2021; then := 1969;
b) If you avoid using all-uppercase characters in your own identifiers your program will never be invalidated at some future time if, and when, new keywords are added to the language.
c) The language designer has the freedom to select the most suitable keyword for a new feature rather than trying to avoid a word that is likely to break existing software.
d) Syntax-highlighting might be useful when working on a screen but monochrome printed program text and code samples in books have yet to disappear altogether.
e) Rather than using a Delphi-like convention of prefixing typenames with the letter T, or the horrendous Hungarian notation, you can use the Oberon convention of starting type names with a capital letter and variable names with a lower case letter:
list: TList;
then becomes:
list: List;
The thought of doing this can perplex Pascal / Delphi programmers who have had no hands-on Modula-2 / Oberon experience. It needs to be tried for a while before deciding whether you like it or not.
- Constants (CONST section) are typeless. They are essentially like macros. That bothers me. Not a huge dealbreaker, I admit, but I'd like typed constants. I know you can always declare variables, initialize them in the "body" of the module, and use them as constants, but they are not proper constants as they could be modified, at least within the module.
I'm not sure that I follow you here. Floating-point constants are REAL, string constants are character arrays, numeric constants are INTEGER. You can't assign a constant to a variable of a different type.
- Related: you can declare variables and make them private to a module, but you can't make variables constant (AFAIR, please correct me if I'm wrong). So, I think that's a limitation.
Stand corrected
Exported variables cannot be directly modified in a client module. There would need to be a 'setter' procedure also declared in the exporting module to enable modification. The setter procedure could then do any necessary validation if required.
- Point I'm not 100% sure of: there are no array or record initializers. So initializing them is very clunky compared to other languages.
True. However, it is rarely a problem in practice for me as I rarely, if ever, use array or record initialisers even when using languages that allow them. When reading code where the record or array would be complex / large enough to make it clunky to initialize it, I have a great deal of difficulty comprehending which part of the array / record an alternative constant initialiser maps onto. While I accept that it would be irksome to those who like such initialisers I would be surprised if it was considered to be a deal-breaker.
- Usually not much of a problem for embedded development (but Oberon is a general purpose language), but a bit more for higher-level stuff: there are no dynamic arrays. Wirth actually removed them from Oberon-2 IIRC. C doesn't have anything like this either, and you have to implement them by hand, but it would have been nice to have them in Oberon.
True. However, I wouldn't recommend using Oberon-07 for anything other than embedded development. A programming language is a tool. Every competent programmer should understand the pros and cons of several languages and choose the most suitable tool for the task in hand. e.g. I use C# and Component Pascal (a superset of Oberon-2) for Windows GUI development such as the Astrobe IDE. I wouldn't dream of coding it in Oberon-07 any more than I would dream of coding it in C.
- Related, dynamic memory allocation is all based on a garbage collector. I don't reject GCs in all cases or as a principle, but I think it could be nice if it could alternatively use a static allocator instead.
A garbage collector is not mandated for Oberon-07. The Astrobe Cortex-M compilers have a safe, restricted, 'Dispose' function that works in a last-in / first out basis. Source code of the system's Memory Allocation module is available - it's only 80 lines of Oberon code so is easily understandable and modifiable.
- Something C was long plagued with as well: there are no fixed (and standard)-width integers. No unsigned integers in general either, except for the BYTE. So IIRC, you have BYTE (which should be an 8-bit unsigned integer on most platforms), and INTEGER, which is a signed integer. The width depends on the platform. If you need to define and/or access integers with a specific width, look elsewhere... you may have to resort to the SYSTEM module. Now see below as well.
An actual parameter declared as ARRAY OF BYTE can have ANY data type passed to it as long as it has the same size. You can then perform BYTE operations within the procedure. Although this is currently implemented in both the ARM Cortex-M and RISC5 FPGA compilers it is not yet defined in the Language Report so should currently be considered as a language extension. In Astrobe we also implemented built-in standard procedures BFI and BFX for bitfield insert and bitfield extract operations. These are extremely efficient on ARM Cortex-M processors because they directly use the corresponding BFI and BFX Thumb-2 instructions as inline code. Examples of this can be seen in the BitFields discussion on the Astrobe forum:
https://www.astrobe.com/forum/viewtopic.php?f=3&t=564- Probably a lot of other shortcomings I can't think of at the moment.
Yes - you have covered most, if not all, of the common gripes. Every language has pros and cons. Much of the time these are subjective, Either the cons become insignificant over time or they become so bad that you swear never to use the language ever again. I won't mention any names ;-)
Anyway, don't take this as Oberon bashing at all.
Not at all. Thank you for your feedback. I very much appreciate the time and thought you have put into it.