I disagree with some of their reasons. Modern: Object Pascal isn't a modern language. It was modern in 1998, maybe, but it hasn't evolved much since then. Latest big change was the addition of generics, behind almost any other language. Fast: FPC doesn't generate particularly fast code, and the nature of OP objects doesn't help with locality. It's faster than scripting languages, but generally slower than AOT compile…
> On the other hand, the ecosystem is great. Hard disagree. I worked with Pascal for about 10 years, and the lack of modern libraries was a source of frequent frustration, meaning we often had to develop the solutions ourselves, or abandon an idea entirely.
Why use Pascal?
41–50 of 516 posts
Re: Why use Pascal?
#42I would meditate on that fact if I was Pascal evangelist, instead of reposting such texts over and over as years go by.
Re: Why use Pascal?
#431. Lazarus. A game engine is -waaay- more than just a 3D engine with the tools being a very important aspect. Lazarus and LCL provide a rich and well featured WYSIWYG RAD IDE and framework for making desktop applications. As a bonus Lazarus as an IDE (even ignoring the LCL framework) is very fast.
2. The language is decent and i know it. This is important because i'm not interested in learning a new language or framework or whatever while also making the engine, i want to focus on one thing. Also i have a lot of code written over the years to cherry pick (i started the project in 2020 but some of the code goes back to 2007-2008). And finally it has a strong aversion to breaking existing code - there is a culture of preserving backwards compatibility.
Free Pascal is far from perfect though and TBH i'm annoyed by some of its constraints - some of them being quite pointless too IMO. For example the language has three ways to create compound types: records, objects and classes. They are almost all identical, except each one of them has limitations that isn't found in others, e.g.:
1. Records. They are like C structs. With "advanced records" language submode (enabled more functionality in a backwards compatibility preserving manner) they can also have methods, properties and "management operators" allowing the compiler to insert calls when things enter or exit the scope (so you can e.g. implement smart pointers, RAII or whatever). However they do not have any form of inheritance or virtual functions. They are value types and as such can be put in the stack, heap and their can be part of another compound type.
2. Objects. They are basically records with inheritance and virtual functions, though they cannot have management operators or some other record-only functionality like a variant section (allowing, e.g. functionality similar to C unions). They are also value types and can be put in stack, heap and/or be part of another compound type.
3. Classes. They are objects with extra functionality, have extra the bells and whistles like dynamic/message-based methods, a published section that expose properties and attributes via RTTI, etc. However they are reference types - they can only be allocated on the heap[1] and as such cannot be put in the stack and cannot be part of another type (only references can be stored).
The thing is none of the above limitations have a technical reason - especially those between objects and records. In fact in the compiler source code all compound types are implemented with mostly the same code with some specialization checks here and there, it isn't like each of the above gets its own separate code path.
But this is far from the only limitation. Another is that there is no way to expose a readonly view of an instance - something like C/C++'s "const" - so you can't -e.g.- have a mutable collection as a private field in a class that is exposed as an immutable one directly, so the compiler knows it can generate code for directly accessing the underlying field but not allowing any modifications to it. The weirder - and misleading - aspect is that the language does have some ways of specifying readonly views in some cases - specifically function parameters can use "const" or "constref" (the latter ensures a value is passed by reference, the former lets the compiler decide) which disallows the value to be modified. However if an object is passed like that you can still make method calls that mutate it even though you cannot assign it to another value, which kinda makes the whole thing feel only skin deep.
On the topic of arbitrary limitations and types, there is also a weird limitation on properties: you can have property getters (and setters, but what i'll describe makes sense only for getters) be either fields or functions. For the former the compiler will generate code that'd be equivalent to accessing the field directly, for the latter it'd be a function call. When you define a property you also declare its type - but if you use a field as a getter it must much the type exactly, on the other hand you can have a function that returns a field as another type. This means that you cannot work around the "lack of const" limitation for collections by making a base class collection that only provides a readonly view and a subclass that allows mutation and then expose the mutable instance as its immutable parent class while letting the compiler generate code that access the field directly - you have to go through a function call and not only the compiler is very stupid at eliminating unnecessary calls, this also changes the RTTI data if you want this to be handled for -e.g.- serialization or via a GUI property editor.
Also when it comes to RTTI which it is still better than what you'd get via -e.g.- C++ (i.e. nothing), it has limitations like only allowing primitive types (so if you have a class for a 3D object and you want to expose its position to be edited via a property editor that uses RTTI to figure out what to edit or to have automatic serialization and deserialization you cannot expose a "Position" property of a "Vector3D" type directly with the RTTI specifying what that "Vector3D" means and instead you have to specify separate "PositionX", "PositionY" and "PositionZ" properties of "Single" type (which is a primitive type for 32bit floats). You can still have a "Position" property of a "Vector3D" outside of RTTI and you can still use a private field of "Vector3D" to store the position and then use getters and setters beneath the scenes to access the individual vector fields for the "PositionX/Y/Z" properties.
All of the above have the end results of using tons of -often inefficient- boilerplate to work around language limitations that in 99% of the cases have no reason to exist in the first place (aside from nobody implementing them - which would be fine, it is a free project, but the thing is the above are things the FPC developers refused to acknowledge as problems - in fact i personally tried to submit a patch that implemented the property case).
As an example, in my 3D game engine i have a "dynamic array" generic (an incredibly common container you'd find in game engines) that i want to be able to allocate on the stack, heap or part of another object, i want it to separate allocation from number of elements (the language itself has dynamic arrays but they always allocate the same number of elements as the array has which means when you insert items you do a lot of allocations and deallocations - that not only fragments the heap but the FPC memory manager isn't that fast in the first place) and of course being able to automatically finalize (i.e. destroy) any managed objects (e.g. a dynamic array of strings with string being copy-on-write "smart" objects defined by the language itself).
In order to get the "allocate on stack or part of object" i couldn't use classes, only objects or records. Because i wanted to expose it directly but without allowing mutation, i really could only use objects since records to not support inheritance. However objects do not support management operators so i'd have to explicitly initialize and finalize the objects. So the workaround there was to store the data in a separate type that is a record with management operators and specify a single field in the (immutable / readonly / base) object of that separate type so that when the object goes out of scope or is finalized itself, its contents are also finalized (remember that i mentioned how this is an arbitrary limitation? The compiler already knows this can happen and handles this, otherwise this workaround wouldn't work!). And also because i cannot have the mutable field accessed directly as its immutable/readonly base type, wherever i use it and i want to expose it i have to use a method getter which returns a pointer to the underlying field (but as the immutable type) - this is needed because otherwise the getter would return a copy of it (remember these are value types).
[0] https://i.imgur.com/zzIH4dl.png
[1] (there are workarounds like using a proxy array with in-place initialization but they are very bug prone so i ignore them as they are not practical)
Re: Why use Pascal?
#44I'm using Free Pascal for my 3D game engine (recent-ish screenshot[0]). For me there is really two main simple reasons: 1. Lazarus. A game engine is -waaay- more than just a 3D engine with the tools being a very important aspect. Lazarus and LCL provide a rich and well featured WYSIWYG RAD IDE and framework for making desktop applications. As a bonus Lazarus as an IDE (even ignoring the LCL framework) is very fast. 2…
These are some of the annoyances with Free Pascal as the language, but there are others like how a unit (think module in other languages) has an "initialization" and "finalization" section that is executed on startup and shutdown respectively. This is good and useful thing, however the problem is that the initialization also has implicit code that zeroes out any global memory in that unit. This means that if a unit A's initialization code runs a function from unit B that accesses some global in unit B but the initialization code in unit B isn't called yet, any modification made by unit A calling that method would be lost ending up with weird bugs. The order the initialization code runs depends on the order the units are used in a program but if there are circular dependencies this order can be whatever the compiler encounters first. The workaround for this is to make sure the program (not any unit) that you compile has in its "uses" section the units that you don't want them to modify and make sure they do not have any dependencies themselves.
Found that the hard way because in my units the initialization section contains RTTI registration calls that were later clobbered by the RTTI registry unit somehow getting initialized after some units that registered some types - this ended up with some assets not being loaded in the engine because the registry couldn't find the type of some serialized objects (because these types happened to be registered before the registry's own initialization code zeroed out its globals and erased any registered types).
The obvious fix would be for the global zeroing out to happen before any initialization section code is executed (and that only in systems which actually need it), but for whatever reason FPC doesn't do that.
On the positive side the compiler is relatively fast - it takes about a couple of seconds to build the full engine + editor on my (now 5 year old) PC. And while there are issues like i mentioned above, the positives (which include the fast IDE, rich framework - and also i had a much easier time when trying to contributing to Lazarus - fast compiler... and the fact i already have a lot of FP code and know the language) outweigh the negatives.
It isn't like there are many alternatives anyway, especially when it comes to something like Lazarus (of which the only potential alternative i can think of - which i haven't explored much - is Qt Creator but i find Lazarus both the better IDE and LCL easier to work with than Qt, not to mention how FPC is waaaaay faster than G++ or Clang++).
Re: Why use Pascal?
#45Re: Why use Pascal?
#46I disagree with some of their reasons. Modern: Object Pascal isn't a modern language. It was modern in 1998, maybe, but it hasn't evolved much since then. Latest big change was the addition of generics, behind almost any other language. Fast: FPC doesn't generate particularly fast code, and the nature of OP objects doesn't help with locality. It's faster than scripting languages, but generally slower than AOT compile…
You're selling it short.
Firstly, as the other poster downthread pointed out, it benchmarked as fast as C++ in the past.
Secondly, it's main use is local gui apps, and there's nothing I've seen, including C# gui apps and have gui apps, that even comes close to how snappy it is.
So I am curious what benchmark you used to determine that it's about 50 times slower than it actually is.
Re: Why use Pascal?
#47I disagree with some of their reasons. Modern: Object Pascal isn't a modern language. It was modern in 1998, maybe, but it hasn't evolved much since then. Latest big change was the addition of generics, behind almost any other language. Fast: FPC doesn't generate particularly fast code, and the nature of OP objects doesn't help with locality. It's faster than scripting languages, but generally slower than AOT compile…
FPC added generics 17 years ago, that is far from recent. Also i'd say that anonymous functions and function references (closures) are better candidates for "big change that was added recently".
> FPC doesn't generate particularly fast code, and the nature of OP objects doesn't help with locality. It's faster than scripting languages, but generally slower than AOT compiled languages, even those with GC.
In practice the performance is fine and you can actually optimize the code as much as you need for any hotspots you find - it can be a bit more of a PITA if you use the "high level" classes compared to C++ but it isn't impossible.
Though if you really want performance out of the box with minimal effort from your side, there is a new LLVM backend. You need to compile the compiler from source to enable it as the entire runtime library, FCL, etc need to be built with the LLVM backend, but that takes only a couple of minutes. On the other hand the compiler becomes much slower (and IMO the difference in performance isn't worth it).
Re: Why use Pascal?
#48Re: Why use Pascal?
#49first word they use to justify using Pascal is that it's "modern". Gave a glimpse at the code source screenshot. No, that syntax is very much the past.
Re: Why use Pascal?
#50Why? Old languages must remain around for legacy support. However, it's hard to see the point of shoe-horning all those modern features into an old language. I write a lot of Java, and really, almost everything since Java 8 should not have been added. Lambdas, for example, are a kludge in Java. Want modern features? Use a modern language. If you want to stay in the Java ecosystem, for example, you could use Kotlin. I…
Java the language was misguided in its decision to use libraries instead of language features leading to impressive amounts of boilerplate. Yes, you can do everything you can do in Python, Lisp or Haskell. But, you can also do that in brainf*ck, by definition, so the actual complexity of Java solutions is actually higher than a more featured language, because you end up reimplementing all those language features anyway in a slightly broken way or pull in tons of dependencies that do that for you, in either case with added API surface to learn.