The "Global build" section shows off how easy it can be the best.
More complicated stuff with modules might be harder especially when dealing with NPM build stuff, but it's generally pretty nice.
* Declarative: here is a UI that I designed ahead of time. I've set up these controls with these hooks. Your logic then plugs into my UI independent of it's design. (When myBtn is clicked, it fires MyCode::MyBtn() in your code).
* Inperative: here is my main.c. In my main(), I call a function createWindow and then createButton with various parameters (size, text caption, styling, etc) that returns a handle to the widget/window. I give a callback to my widgets. When that button is clicked, the callback is fired. I can call a function called destroyWidget with that handle and erase it.
Or, to simplify solely to definitions: in declarative UI, I create (or declare) the UI ahead of time and hook it into my logic. In imperative UI, the UI is rendered as requested (an imperative). The definitions are directly orthogonal to imperative vs declarative programming, in general.
As to "reactive", this is an independent descriptor that simply means that the UI adjusts to it's environment. It's usually used in the context of Web or mobile devices, since an Android app that appeared the same on Phones and Tablets would get a subpar experience for users on one or the other.
Given an imperative UI and a small amount of time (depending on the surface area of the API) I can create a declarative interface to it.
I cannot do the reverse given a declarative UI.
My observation of declarative UIs is that, sooner or later, due to necessity, they tend towards the inclusion of imperative elements ("If we are running as Super-User, then $FOO must be a button, else it must be a link to a page requesting Super-User privileges").
At the end the "declarative" UI is declarative for only the most trivial usages. Any non-trivial usage includes hacks or workarounds to implement conditional elements, loops (for collections of elements) and subcomponent specialisation (for element reuse).
if condition then ConditionalWidget1 else ConditionalWidget2
Where this if-statement is embedded in a declared UI/widget tree. That's hardly a hack in my opinion.
There are similar ways of building a dynamic list of widgets declaratively which don't feel hacky at all. For example, look at ListView.builder[0] in Flutter. You provide an array and a template function of how each element in the array should look and then you have your dynamic list of widgets. (Of course more complicated use cases require more complicated code, but it's still not hacky and fits well with the larger sstem.)
[0] https://docs.flutter.dev/cookbook/lists/long-lists
I might be misunderstanding your definition of declarative vs imperative but I don't think this way of building UIs is hacky in any sense.
AIUI, declarative is listing what you want, while imperative is listing the steps to get what you want. Naturally, I feel that extending a declaration with conditionals or looping constructs make that not-declarative anymore.
That's because, I feel, a conditional is a sequence of steps, not a declaration.
Hence I regard (perhaps incorrectly) that declarative syntax with support for conditionals is "hacky".
(The XAML way of expressing conditionals and loops does feel hacky to me since you need the code and markup to communicate with each other instead of expressing directly in code.)
Edit:
Here is a well-considered blog post from one of the Uno Platform contributors about UI-as-code vs markup if any are interested. https://platform.uno/blog/markup-vs-code-for-ui-angled-brack...
Because GP is criticizing Win32-style imperative UI code... and this is exactly what Delphi saves you from.
From a webdev point of view I love Vue...but it is a slow hard way to build UIs compared to Visual Basic, Delphi, or Swing.
MFC had a mix of generated code and handwritten code. Writing MFC code by hand was hell, and using a graphical IDE was nowhere as convenient as Visual Basic or anything by Borland.
The Visual Studio 6 box had an introduction book to win32 and MFC ... Nothing made sense to me, a lot of boiler plate code was there without much explanation about why it was there. I just said "nope, life is too short to suffer this", and went back to DOS. Learned Unix system programming at the university, it felt sane and consistent.
This introduction to win32 API was so traumatizing, even now, I would not touch it with a barge pole, POSIX or GTFO.
Although, you're going to have to be fairly proficient to get your head around that stuff too.
Agree that X11 has a dreadful API.
And that O'reilly X Windows book series, The Definitive Guides to the X Window System, a "tiny" set of 8 books, only for the UI stack.
X Windows with the X Athena Widgets, or Motif, you better have all those books.
Some of this is a symptom of the fact that Microsoft was still fumbling in the darkness regarding its Windows developer experience.
First generation windows programming was a complicated affair involving a number of command line tools, textual languages, and switching back and forth from the GUI to DOS. The first Visual BASIC product fixed all of that, and made it possible to achieve good results with just point and click and (more or less) scripting code.
My read on Visual C++ (predecessor to Visual Studio) was that Microsoft was pushing hard to graft the Visual BASIC style of point and click development onto its old school Windows development tools. The result was an IDE with a form editor that could superficially work a lot like Visual BASIC. You could drag a button, double click it, and be dropped into a blank C++ (rather than BASIC) function to handle the click event at runtime.
They did this with specific features in MFC, but also with a huge amount of default code generation. Double clicking that button in the form designer would update message maps, write a function, and make a number of other changes in the source text of what was already a large body of generated code.
The trouble is that while this worked, it didn't actually teach people what was going on. The development environment wound up being very brittle and hard to understand, particularly if you did anything that confused the IDE integration.
I suppose it could’ve been a bit of both.
The preceding version, MSC v7, was the first version of the Microsoft C compiler to support C++. It's also where Microsoft introduced the first version of Microsoft Foundation Classes.
My understanding of the history of MFC is that MFC is Microsoft's second attempt at a C++ class library for C7. Prior to MFC, they had developed a significantly more object oriented framework, but found it in testing to be confusing to developers of the time. (Who had just ascended the Win16 learning curve itself.) Micsoroft retrenched, and the MFC 1.0 they shipped with C7 was a much thinner layer over the Win16 API than what they had initially planed. (The earlier framework was AFX, which is why there are AFX prefixes in the MFC codebase.)
My presumption with respect to Visual C++ 1 is that Microsoft found themselves short on time and facing dual mandates of maintaining MFC 1.0 source compatibility and producing a C++ development experience a little like Visual BASIC. So a bunch of IDE trickery and codegen logic was the logical path forward. (IIRC, the Visual BASIC compatibility mandate extended as far as enabling VB custom controls to work in Visual C++ projects.)
I haven't used it, but Borland Delphi post dates all of this, and gets it better. Borland was able to specifically build a class library and extend the programming language itself in a way that supported visual design tools. My understanding is that this can give some of the Visual BASIC style point-and-click, but also makes it easier to delve into the underlying framework code and make your own components. (Also important to note that Delphi was Borland's second attempt at a Pascal based windows development tool.... the earlier Turbo Pascal for Windows was a lot closer to the Win16/MFC-like experience)
Yes, the auto-generated boilerplates was stupid (and obscured so much) but it was their way of trying to get people ahead in an "easier" way.
Where the nightmare started is the X toolkit. It slathered on three extra layers of complexity before providing any benefit for the programmer and was so damn confusing even when you did get it working. And what did you get out of it? Athena widgets.
GTK is a dream in comparison.
As a kid, I wanted to make games, and just found the win32 API inscrutable. I too lost a couple years.
Charles Simonyi comes to mind.
Instead they're the result of a whole bureaucracy of people each of whom have to inject their own particular requirements, coding preferences and "what if in the future..." guesses.
my verdict is that Simonyi didn't communicate the idea - good otherwise - well enough
As for Apps Hungarian, I was just discussing this the other day on a forum devoted to John Ousterhout's A Philosophy of Software Design. In Chapter 14, "Choosing Names" Ousterhout discusses a case where a variable named 'block' was used in a place where there were both physical and logical blocks, both integer types. His suggestion, which isn't wrong, was to use different variable names for different kinds of blocks, making it possible for a programmer to tell if a logical block variable was being used in some physical block handling code. This is similar to Joel's example of using 'rw' and 'col' prefixes for variables that are both integer types.
When I see a convention like this, I ask, "why can't they be different types?". In modern languages you can define new types easily, and define different interfaces or APIs to operate on the types. With 'physicalBlock' and 'logicalBlock' types, or 'row' and 'col' types, programmers can lean on the compiler or interpreter to prevent many errors. Beyond looking wrong to the programmer, the code is wrong, and won't compile or interpret correctly, provided there is a reasonable type system for the language.
The obvious concern is a proliferation of types, and yes that can happen when taken to extremes. One area where Joel's example makes a very strong case for types is 'us' and 's'. Strings are handy, but also the wrong abstraction. A 'SafeString' type would very clearly communicate the abstraction at the proper level: the thing in question is not a string, it's a representation of encoded user input. An attempt to assign a SafeString to a plain old string would be an error. Passing a string to a function that declares a SafeString would be an error.
There's even a name for using ints and strings when different types would be better: Primitive Obsession. A sign of this obsession would be passing around a user ID and password as strings, and a corrective would be a Credential type that encapsulates both ID and password types, and includes the necessary parsing and validation on creation. This eliminates an entire class of errors much more strongly than a convention where every string that contains a password has a 'pw' prefix, and userID strings are prefixed with 'userid' or just 'id'.
Of course, if the language doesn't provide useful typing, then naming conventions are essential. In languages with good type systems, always be asking, "does the language's primitive type really represent the abstraction in use here?"
The Mac OS from the 90's had zero POSIX, and was less capable than Windows, hence Copland, which by the way also had zero compatibility with UNIX and was going to be a microkernel OS with C++ userspace.
Also of course that’s Microsoft of the late nineties: e.g. COM/OLE2 originally had a Macintosh port and an obscure commercial-Unix one, VC++ 4 shipped with a Macintosh cross environment complete with an MFC port, etc. Don’t know if anybody ever seriously used those except the Mac ports of Microsoft software (Office, IE).
Some of the key problems that makes Win32 unpleasant to use:
1. Preferring to be consistent with past mistakes rather than fix them. Core Win32 isn't namespaced in any way, and is full of generic function names like "StartTrace" that can easily conflict with user code. 30 years later they are still adding new Win32 APIs that also aren't namespaced in any way. They still use the "Ex" convention and so on.
2. A pervasive assumption that the APIs shouldn't have any opinion about memory allocators or heaps. You get this in old UNIX APIs as well. Why does the ETW API mocked in this article require so much pointer arithmetic and memory munging, well, because Microsoft don't want to have an opinion on where memory should be held. They see malloc as a utility convenience, so nothing in the API will malloc memory for you, or if it does, then it's done using its own API specific wrappers around malloc/free. By implication Win32 generally doesn't have functions that construct objects for you, instead it's always up to you to allocate the memory and pass it in. That means the API might be called with allocations of various different sizes depending on when the app was compiled, so, then the caller is typically expected to place the structure size in the structure itself as a crude form of versioning. It's all a lot of tedious boilerplate that's easy to get wrong.
3. An inconsistent and ad-hoc (API specific) approach to versioning and backwards compatibility. There just doesn't seem to have ever been much planning ahead. Win32 is full of places where they left room to extend the API yet never used them, and places where they didn't and then needed to introduce an Ex or 2/3 call. Again COM introduced better ways to manage this problem, but they didn't use that consistently either.
4. Frequent use of random GUIDs to namespace things. The article has examples of this. Nowadays we take for granted internet based namespaces, like how Java/macOS use reverse DNS names to identify code. Win32 originates in a pre-internet era, more or less, so a lot of APIs are built on the assumption that nobody can communicate with anyone else and there are no central naming registries that work. Giant random numbers are a reasonable solution to this, but again, Microsoft stuck with it for way too long after the concept became obsolete.
COM fixes some of these things but is used inconsistently and it was never retrofitted onto the core API properly until WinRT. For example, if you want to work with Direct3D you need to use COM and instantiate some COM objects, but if you want to then connect that Direct3D surface to a window, there's no COM object to represent a window, that's all done with legacy C APIs. Why did they never switch entirely to COM? Probably internal politics - developer experience became seen as some other department's job (VC++), and Bill Gates became obsessed with Big Chunky Features he could easily understand without doing Windows programming as his day job (like WinFS).
That said, some parts of Windows API are horrible; everything COM-related comes to mind.
Most people who stayed with win32 for long were C programmers who preferred the "purist way". But those who wanted developer productivity soon switched to the frameworks of .NET, Java, Python, etc.
Also many of us already in the 16 bit days would rather use C++ frameworks than raw C.
One really needs to be quite masochist to use pure C in Windows.
So no longer .NET, rather .NET version XYZ.
https://en.wikipedia.org/wiki/.NET_Framework_version_history
=> NET Framework 1.0 is an integral component of Windows XP Media Center Edition and Windows XP Tablet PC Edition. Installation CDs for the Home edition and the Professional edition of Windows XP SP1, SP2 or SP3 come with .NET Framework 1.0 installation packages.
Unfortunely WinDev seems blind to how it is perceived from outside Redmond, is keen in sabotaging DevDiv efforts to make it usable, and keeps pushing it everywhere, moreso after Longhorn.
But on the upside I got a lot out of my system in my 20s, and have never gotten burnt out on programming.
Once.
I also felt it was unreasonably hard in MFC/Visual C++ to just have a button in a different colour. The RAD system was so barebones compared to what I was used to previously with Visual Basic, and the people in Delphi could make vibrant, colourful GUIs which were a pain in the butt to do in C++.
I will always remember Delphi as the cool RAD system that could create beautiful GUIs but the language is a bit ass, so I never took the time to learn it. (I have never liked the feel of Pascal for some reason)
I was 19, and I was a member of some forums where folks shared knowledge on how to (ab)use various technologies for profit; many of these technologies were Web-based. I didn’t know any Web programming, but I knew VB.NET from some community college courses (with some fundamentals from high school) and I figured out how to drive this browser and interact with pages.
This allowed me to automate various Web interactions and make a few bucks writing code, which eventually led to my current career as a professional software developer.
I was getting paid (not much lol) to build apps before I knew either of brands like “port” but I hope that, no matter how much money is in the table, y’all feel good about every deal! Respect
I remember having similar struggles. Windows programming was a huge lift. Not only were there new API's to learn, it was really an entirely different and novel architectural style.
Kerry Nietz writes about the experience of porting FoxPro from DOS to Windows... his description is apt: "Turn the program inside-out". For me, that was hard to do at a time when I was still working through how to make a normal program.
I never did learn win32...
Then I discovered HTML - much easier to wrap my head around at the time.
When I was a kid I tried to build a windows app. Getting a notepad clone even kind of close was a triumph for me, but I decided I wasn’t smart enough to be a programmer, and it set me back about 8 years at least.
I was about 19 and a jr sysadmin changing air conditioning units when I finally sorted out the vast majority of coding is vastly simpler and vastly more fun than win32. Those windows APIs cost me a lot of time!