07/08/2023

QM: Why don't I have the patience to learn programming?

Why don't I have the patience to learn programming?

This question is part of the reason.

I’m not suggesting it is your fault. I’m saying that prospective programmers are taught to sabotage themselves, and many quite reasonably decide to give up.

The darker path

No, it's the question itself that's at fault. “Why don't I have the patience [required] to learn programming?”

That question is a trap.

It assumes impatience is the reason we fail. It blames an intrinsic, undefined trait. It comes with its own conclusion (“I don’t have [the innate ability x], therefore I can’t ever learn [the skill y]”). It suggests the issue stems from an intellectual or moral failing.

In short, it’s just not a helpful approach to learning something new. Every time you ask yourself that question, you reinforce the same ideas:

  1. “I should learn ‘programming’.”
  2. “I don’t have what it takes.”
  3. “Learning to program is hard and demanding.”

None of them help. All they do is hold you back.

I know it doesn’t feel that way. Trust me, I know. I’m a C++ programmer with ADHD and apathetic tendencies. I can’t say for sure how you work, I can only infer it from the question itself, but I think I can help.

And I think the best way to help you is by shifting the way you look at programming and learning.

Reframing

Yes, patience helps when you code, coding can be hard, and not everyone is equally gifted at it. None of that stopped me. In the end, none of it will stop you.

“Learning programming,” too, is a passive, empty concept. Make it active and meaningful. You want to read cool books about exciting stuff — you do not want to “learn reading.”

One more thing (and this is important): I’m not saying your attitude is what makes a coder. “Positive thinking,” the idea that imagining it hard enough will make it happen, is harmful bullshit peddled by charlatans. The idea that “if you struggle at it, you’re a failure” is toxic nonsense. Saying you just need the “passion” to “realize your dreams” is crap, too.

The right question

So don’t bother with the question: it has no useful answer. You already have the question you need, which is far simpler: “do I want to make something fun on my computer?”

If the answer is “yes,” then do it.

Have someone install Python on your machine, and teach you how to print something to the screen. Now have the program ask for a name, and print it to the screen. Your name. Run it. See it. You built this.

That’s fun, right? Make the text repeat itself. See if you can get a graphical figure (maybe made from letters) on the screen. Make it move. Play with it.

Build a maze. See what happens. Save all the time. If the program crashes, that’s alright. If something doesn’t work, great! We’re playing. We’re experimenting. We’re discovering new things and making pleasing shapes on the machine.

We hit walls. We trip over details we never noticed. As we do, we learn new patterns and nuances, and deepen our understanding. We’re stretching our ability, getting frustrated, then beating the obstacle, and that feels good.

And then we do it again.

The end note

What you just read is programming. Can you learn that? I think you can. As long as you’re playing around, relaxed, without judgment, programming doesn’t have to be harder than that.

Play. Laugh. Learn. Share it with friends.

Above all, fail and fail often: that’s how you improve.

You’ll get there, and if you don’t, or if it doesn’t happen quickly, or if you feel embarrassed, then remember: it’s not a big deal. It says nothing about your value as a person.

It’s just coding. That’s all it needs to be.

10/09/2021

QM: Do you think C++ is beautiful?

Do you think C++ is beautiful?

God no. It works. It’s powerful. But beautiful? No.

C++ is all power and sharp edges. It’s pretty much a Brutalist fortress composed from wildly disparate architectural viewpoints. Syntactically, it’s rather cluttered, with no real unifying principle. It’s a bric-à-brac if you’re generous, and a crash site if you’re not.

However, C++ is much better than it used to be. Original C++ was very messy. Expressing yourself succinctly in it was a difficult task. C++17, by contrast, is straightforward, and allows you much terser and more orthogonal constructions, safer memory management, less clutter, etc. Consider iterating over a vector in C++98:

for(std::vector::const_iterator p= pairs.begin() ; p!=pairs.end() ; ++p) { 
	std::cout<< p->name<< ": " << p->val<< '\n'; 
} 

Now the same thing in C++11:

for (const auto& p : pairs) { 
	std::cout << p->name << ": " << p->val << '\n'; 
}

And in C++20:

ranges::for_each (pairs, [this] (auto& p) 
   { std::cout << std::format("{}: {}\n", p->name, p->val); });

Now adding some usings and new capabilities, we get this:

for_each (pairs, [&] (auto& p) { 
   print("{name}: {val}\n",
      "name"_a=p->name, "val"_a=p->value);
});

Dismissing that as mere syntactic sugar misses the point, which compared to older C++ versions and C is this:

  • Modern C++ lets you express yourself with less noise.
  • Modern C++ lets you write code that is more generic.
  • Modern C++ avoids entire classes of error, particularly when following common patterns and conventions.

C++ is heading somewhere. It’s converging on a leaner, tighter subset. Despite the requirement to be mostly backward compatible with C++, we’ve already seen careful excision of things like diacritics and the virtual deprecation of #define, bitwise arithmetic, spurious operator overloading et cetera.

But C++ wasn’t written to be beautiful, and it shows. Even the idealized C++ that Stroustrup is clearly striving toward is an unlovely, brutish thing. But sometimes, for some jobs, you need a combine harvester rather than a Camaro.

Lisp, Smalltalk, Prolog, Haskell, Python — these could be said to be beautiful. In my opinion, beauty follows criteria that neither C nor C++ satisfies:

  • Conceptual elegance. In Lisp, everything is a list. In Prolog, everything is either a world fact, an implication, or a query. In Smalltalk, everything is objects and messages. Haskell wants everything to be a mathematical, stateless function chain, where Python just wants everything to be as simple as it possibly could. C++ does have a philosophy, but it’s murky and syntactically impenetrable; C has “I’m portable machine code!” which really is more of a mission statement than a coherent concept.
  • Terse, expressive syntax. Smalltalk can famously be described on the back of a postcard. Lisp and Forth can be derived from almost nothing. C is pretty good here in the terseness metric, although I think its legibility is overstated. Prolog, similarly, does much with very little. By contrast, while it’s possible, you have to really know your C++ to consistently produce terse and idiomatic code.
  • High level of abstraction. A beautiful language is one in which you merely have to model the problem in the domain of the language to have the solution suggest itself. Again, not a C++ hallmark unless you’ve learned how to do this as an orthogonal discipline, and C provides virtually no abstraction other than that of memory and flow constructs.
  • Legibility. If not for this metric, Forth (and possibly Perl) would probably make the list. A truly elegant language is not cryptic but can be read and understood in a straightforward way. Code written in C and C++, by contrast, can be famously illegible.

There are other metrics, but these are the ones I value. In closing, however, I have to say that while elegance is always a desideratum, it is not a requirement. Sometimes, tackling a big, sprawling language can be a fun challenge, while a more streamlined version of the same thing would just feel empty and impersonal.


Notes on the Quora Migration: this piece originally appeared on Quora. Since Quora is no longer what it was, I'm migrating my content here.

QM: The bare minimum

I have an old computer having having intel pentium processor with 2.00 GHZ speed, 1GB RAM and only 150 GB hard drive. I have a passion to learn programming. Can I learn with this device?

This, my friend, is the Commodore 64.

It was launched in the year 1982 at a price point of $595 ($1,500 in today's money). Its 6510 processor ran at roughly 1 MHz, it had 64 kB of RAM, could be fitted with a casette tape player or (if you had the cash) a floppy disk-drive. It had no true operating system, but booted you directly into a BASIC interpreter. As for hard drives, Internet, sample-quality sound or graphics with more than 16 colors, those were pure fantasies at the time, about as attainable and practical as ordering a unicorn by mail. And you know what?

It probably created more programmers than any other computer.

Yes, it was limited. That's actually a point in its favor, because we learn and grow as programmers by facing and overcoming limitations placed in our way. And to varying degrees, as many as ten million C-64 users did precisely that, because they had ideas, because they were challenged by the scarcity of resources, and because the barrier to entry was so low.

So. The computer you have is thousands of times faster, with over ten thousand times the memory, and nigh-infinite permanent storage that does in nanoseconds what would take minutes to load or save onto precious tape on the '64. Its Internet connection lets you download, for free, all the development tools and examples and tutorials you could ever need. With the proper OS, you can have it multitasking like a dream, and you'll never have to deal with not having memory protection, virtual memory, or any of the thousand things that didn't exist during the eighties.

Right, the conclusion. Well, the question was, "can I learn with this device?" And that is of course entirely up to you. But the idea that the computer itself would limit you is just... no. Compared to what most programmers throughout history used to learn their craft, the one you have is amazingly, mind-blowingly, ridiculously overpowered.


Notes on the Quora Migration: this piece originally appeared on Quora. Since Quora is no longer what it was, I'm migrating my content here.

QM: Do good programmers use Else?

Coming from a C/C++ legacy codebase written by self-taught engineers, I understand why someone would embrace this notion. You can only look at so many gigantic piles of nested if statements before you go "you know what? I'll just collapse the whole pile by exiting early using return statements." Before long, you'll clear away all these little methods that only branch when their various calls result in a null pointer, and you'll pat yourself on the back for having eliminated all the else statements. Tempting, then, to wonder when else is even necessary.

True, those else statements were redundant. That means very little. All it proves is that like any other tool, else can be misused. That doesn't make it a bad tool, it just means it was used in the wrong place. For short, well-considered routines, if-else is perfectly legitimate.


Notes on the Quora Migration: this piece originally appeared on Quora. Since Quora is no longer what it was, I'm migrating my content here.

18/09/2018

Setting the record straight on AMOS

I feel I have an apology to make.

One of my first posts, and the one people most seem to appreciate, is the one in which I absolutely slated AMOS for the Amiga as a programming language. I went to some lengths on the matter of how poorly I found AMOS to be constructed, how its organization was lackluster, and pointed to the inconsistency of its design and various choices made. Those were the things that frustrated me back when I was a weedy little sprog desperate to code games on the Amiga.

Which is all well and good, but that's not everything that is important about a language. A language isn't just its syntax or its implementation. The most important part of a language is the intangibles. The community. The accessibility. The culture.

AMOS had that. And what's more... it still does. In my post I made mention of François Lionet, the man who wrote AMOS. I paid him what was at best a backhanded compliment, in saying he was an able programmer. Not only did I undersell things (the man wrote a language that touched almost all of the Amiga's hardware, and in a fast but systems-friendly way), but I did the man himself a tremendous disservice.

AMOS had its share of flaws, yes. But if the alternative was not to push it out the door, aspiring Amiga coders would have had a much higher bar to clear.

And perhaps that was why my reaction to it was so negative. As the youngest boy in a well-to-do academic household, I grew up internalizing elitism and my supposedly fated intellectual superiority. AMOS was supposed to teach me how to attain full control over the machine, and yet learning its basics brought me no closer to understanding Assembler or C. I think my lingering resentment toward AMOS may be a reflection of my own reaction to a perceived failing, and the need to reframe AMOS as a condescending play-toy.

If that's true, I'd say it's high time I grew out of it. I now believe that François Lionet was entirely correct on several key points.

  • The Amiga community needed a REPL-capable language that was powerful, reasonably fast, and tailored to the machine. AMOS was that.
  • The playing field needed to be leveled. The Amiga base was largely split into gamers and power users, with few of the latter having the patience to educate the former. But coding should not just be the province of an enlightened priesthood. The Amiga, after all, always held the potential to be all things to all people. For all its flaws, AMOS democratized coding. That is a badge of honor.
  • AMOS is often good enough. It's not weak simply for not being Assembler, and you can pull off some pretty impressive things in AMOS if you know how. That fact is often overlooked, but no less true for all that, because the worth of a program isn't in its presentation. If you have a game that is shit in 16 colors and increase the colors to 256, then what you have now is slightly more colorful shit. What makes a good program? Solid concepts. Great art. Attention to detail. Clever use of algorithms. If your idea is good enough, it should be viable on the Amstrad.
  • Documentation is key. Most Amiga programmers wrote their own instructions, which tended to be confusing, omit key information, and generally required you to live inside the author's skull. Worse for me as a teen was that nobody seemed to acknowledge this, which left me with the impression of constantly missing something important. In AMOS, François Lionet produced a manual that actually made a degree of sense. While it still managed to confuse me in places (mainly in mixing AMOS-specific concepts with general Amiga ones), the manual was very readable, explained most concepts in a way that didn't confuse the reader, and was sensibly laid out.
  • It was inspiring. Where other communities were opaque and grudging, AMOS wanted to welcome you. Back then, I wanted coding to be a secret club, so I scoffed at the lack of gatekeeping. Even then, though, Lionet's honest celebration of aspiring coders shone through, and it was a great motivator. It's a pity I didn't fully appreciate that back then, but I treasure it all the more today. We need a more enthusiastic approach to computer programming and content creation, and AMOS had that in spades.

These factors are, I think, enough to excuse a litany of sins. It was enough to make me join the Amos group on Facebook, where François Lionet himself is a regular poster. AMOS may never be my holy grail, but I've taken to using it when I want to quickly slap some graphics on the screen or construct a table of precomputed values. Explorative development is quite quick, and less likely to crash the machine than Assembler or even C. I still would have preferred a system that didn't reinvent graphics handling or sandbox the system, but the existing AMOS is a flawed but solid product. François Lionet could have left it at that when he left Europress. He's working on Friend OS, and that could easily be his legacy. It would certainly be enough for most people.

Not for François Lionet, though.

François Lionet, it turns out, is more of a visionary than I gave him credit for. He's also a far greater person. Why? Well, aside from unfailing courtesy and unstinting helpfulness on his own Facebook group (which he does not, I must add, treat as his own private fiefdom even though he easily could), and disregarding his work and advocacy for the Amiga-like Friend OS, his sheer enthusiasm and investment in doing right by his fans should be an inspiration for all of us.

You know guys, what prevents me from coming back to AMOS source code and change it, is that I fear it will take me a long time to setup a proper dev environment with UAE, with disc and everything, editor, graphcx editor, assembler... all the necessary tools to work...

Once I have that, I am sure that I can find how to compile again very quickly...

But, if someone does that, that is, make me such machine, and provides me with a zip file with everything installed and ready to run in one click, I can try! It will be really fun to dive again into 68000 code, I will feel 30 years younger! And if in AGA you only allow one screen, full size, and only 16 colors rainbows (the first ones) or something like that, then it is not much to do! I think I can do that in a week-end...

What do you think?

The group erupted into standing ovation. A bit of faffing about ensued regarding just how this thing would be accomplished. But in the end, this was the conclusion, from the mouth of the Lionet himself:

OK guys. You have really cool ideas, and it would be so stupid to leave them get forgotten in the depths of FB.

I will act as an administrator now on this side, sorry, but if YOU want a better version of AMOS, there is no choice.

1. I create a post with bugs

2. You post bugs there, it will come back up if it is active. But I am not making it sticky, this would be really depressing for me to see it fill up every time I connect

3. I create a 'suggestion' post, and this one will be sticky :) (possible Michael Ness?)

4. I will delete each and everyone of the posts with suggestions and bugs after this one. But carry on with conversations please!

BTW: I will not read them after... let me look... Colin Vella (sorry, not read), ' if possible, .... :P

Good to be the king! :D (I'm not, or if I am, a Viking king, not a French one with white powder acting as a lady. Don't come too close to me, I will slash your throat so that you can have fun in paradise :) )

This is what being a true gentleman looks like. So that is why I owe François Lionet an apology: instead of the whining and picking out perceived flaws in his work, I should have celebrated the soul of a man who, 30 years after a computer's demise, still makes good on his promises to its community.

One poster on FB responded to François' offer by simply saying, "you legend!"

Simple, but beyond dispute.

So thank you, François Lionet. Appreciated or underrated, a legend you remain.

05/09/2018

Converting C into Assembler on the Amiga

Introduction

This article is basically a cheat-sheet for converting C code into Amiga Assembler. The basic method is not my own. I found a page on it a year or so back, but unfortunately, I forgot to save the link. This is my rendition of the method, and any errors in thought or application should be considered mine.

Most coders, whether on the Amiga or not, are familiar with the C language. Not everyone knows how to work with Assembler, though, and the bar for learning it can be daunting.

For people already conversant with C who want to learn Assembler, there's a solution: translating it yourself, by hand, at speed. That sounds like a tall order, and complex C is indeed hard to translate. But C doesn’t have to be complex in order to be powerful. If it turns out a reduced subset of C code can help to not only dip our toes into Assembler but to write it quickly and accurately (and I aim to demonstrate that it can), then it should be an avenue worth pursuing.

Our goal, then, is to write our program in C, and then render that code into legal 68k Assembler. At first glance that would be intimidating. However, by adding just a few self-imposed constraints, we can restructure the C code into a form less concise but much easier to translate.

This is a deceptively powerful technique. After all, compiled C is just the computer doing its uninspired best to write Assembler in the first place. It just does it much faster than a human, though sometimes less efficiently (particularly on older systems, where memory and CPU speeds impose constraints on the fancy optimizations the compiler can pull). Still, manual translation remains a proven method of producing a working Assembler program in short order. The added benefit is the greater insight into one's own code offered by manual translation.

From C to A in three steps

The procedure itself is unambiguous. It takes a C listing, and the end result is a rough sketch of the final yet-to-be-optimized Assembler code. We will most likely require a few Amiga-specific additions to get something up on the screen, but the resulting binary will otherwise be fully functional.

  1. Write the routine in C. Determine its correctness.
  2. Restructure the routine in accordance with the following constraints:
    1. Calculations must be on the form of [value] = [value] [sign] [number or variable] (ex: "x = x + 2"), nothing more complex.
    2. Comparisons must be on the form of [number or value] [comparator] [number or variable] (ex: "x != 2"), nothing more complex.
    3. Remove complex branching constructs. Replace with goto or function call.
    4. Remove loop constructs. Replace with goto.
    5. The only allowed conditional is a single-line if statement followed by a goto.
    6. Remove references to variable length strings, arrays or lists. Use statically sized arrays.
    7. Replace primitive functions with calls to OS functions on the target platform when convenient (ie. typedef).
    8. Rename variable names to resemble register names, where appropriate.
    9. Test that the code’s output is unchanged (i.e. is still correct).
  3. Translate this modified code into rough Assembler form, add Amiga-specific code (libraries, interrupts, hardware access) and, again, verify correctness.

How the method is used

To illustrate the method, we could try a simple example. Project Euler is a website that offers a series of problems in roughly ascending difficulty. Most of these problems are constructed in such a way as to make a brute-force solution computationally expensive.

I generally try to avoid spoilers. However, the very first Project Euler exercise should be considered reasonably easy for any CS student to solve in a handful of minutes. The Sieve of Eratosthenes provides a fairly good, conceptually simple solution. Better still, it yields a short enough listing to be manageable for us to translate. Note that will not translate the printout part of the routine, as it would just be a distraction.

Running this program yields a sum of 233168, which in hex is 0x38ed0. This is our expected result.

Next is to prepare the C listing for translation. Applying our list gives us the following modified C listing:

This is already pretty close to what we want, but we're still not there. We need to put registers in place of variables:

Except for the Printf() function, this is practically Assembler at this point. The instructions are now ready to be translated. The result:

Assembling the program results in a minor error, easily fixed by changing the addq instruction in the third loop to add.w.

As noted, the program should result in a value of 0x38ED0 in register d0. When we assemble the corrected listing and run it, it yields exactly that: ergo, the program works. The only bug we encountered stemmed from my attempt to optimize instruction size, which was not part of the method. Had I stuck to the script, the first attempt would have assembled and given the correct response.

Two things bear mention about our C implementation. When I wrote this, I declared three iterators (scoped to each for loop). Doing so is good high-level practice, but runs contrary to the Assembler ethos of porous scope. When preparing for translation, consider deliberately compromising scoped constructs or even declaring variables as global.

Regardless, the results speak for themselves. Judging by the above demonstration, the idea is sound.

14/06/2018

Incremental Tutorials #1: The Bootable Program

I grew up with my dad’s Amiga 2000. It was a brilliant machine that I desperately wanted to understand. The path was daunting, with countless steps and milestones along the way.

Although I did learn a lot, there were some I didn't manage. I kept circling back to one particular goal. The bootable disk. This was something the PC couldn't manage, and that for me somehow encapsulated what it meant to use the Amiga.

It's exactly what the name suggests. You place a floppy in the diskdrive, it loads, and then the game starts. I wanted to make such a disk. It was magic to me, the province of mighty game coders, and I was a kid dreaming the dreams of Icarus. I never did succeed, not back then. But as it turns out, it’s never too late to start.

Programs on the Amiga usually came in one of two categories. Either the program was meant to be used under Workbench (generally, that meant it was a utility), or it was designed to boot from floppy and would immediately kill the system in order to maximize those resources for running the program (this was usually the case for games). We will probably go into how to do that as a later refinement, but it all starts with this first lesson, which is booting from floppy.

Well, turns out it's not a difficult process. To that end, I’m making this our first incremental tutorial. It has but one, simple goal: to let you create a bootable disk containing a typical game or utility for the Amiga 2000.

This tutorial will be simple and rough. It will not be optimized in any significant way. The idea is to master the basic technique first, before we return and refine what we have into something smaller and punchier.

First iteration: the floppy that boots

Overview

This will be the first tutorial. The intended end result is a disk that will boot into an already existing program. This requires

  • adding a boot-block to the disk,
  • adding a program for us to boot, and
  • writing a suitable start-up sequence.

This is a reasonably straightforward task. I will stress that I'm assuming you use an emulated machine, as logistical issues of running on real hardware (like transferring ADFs to floppies, etc) won't be part of this tutorial.

Step One - adding a boot-block

All bootable disks start with a blank floppy. If you're using physical media, that's any old floppy disk you have lying around whose contents don't matter to you. If using an emulator, create a disk image such as an ADF file (UAE has a button for this).

Now, the next thing you do is boot your Workbench system disk (version doesn't matter). Once WB finishes loading, open a CLI window, and start typing.

format drive df0: name Lesson1_1

What follows is a prompt to format the disk, so pop out the Workbench disk. Insert the empty disk (making sure it's not write-protected), and hit return. Formatting will, unsurprisingly, wipe the disk, erasing all its data and leaving it blank. Next, we want to ensure the disk boots when you insert it into Df0: on startup. After the drive's settled, type:

install df0:

You'll be asked to swap the disks, but the program itself takes no appreciable time to run. That's basically it; you now have a disk that autoboots. Go ahead and try it by pressing CTRL+Amiga+Amiga. Once you're done, boot back into Workbench.

Step Two - adding a program

The idea here is to start a piece of code that doesn't need running under Workbench. My rationale for that is the fact that bootable disks often avoided using things they didn't need. In other words, we don't want to tie ourselves to having a running Workbench process. Following this stripped-down ethos, we want a dead simply program, one that doesn't use extra libs or indeed more than one executable. It seems pleasingly symmetric to use Pong for this, so we will: this PD version of Pong by Claudio Buraglio from 1991 satisfies all the conditions.

If you're running an emulated Amiga, transferring the binary is just a matter of loading the ADF like any disk. A real Amiga makes it a bit harder: I would recommend the PCMCIA to Compact Flash solution, a Gotek drive, or (if you're feeling old-school) a null-modem cable. Transferring it to a real machine can be nontrivial, but, again, beyond the scope of this tutorial.

Next, copy the Pong file to the disk.

Step Three - adding the startup-sequence

The final step is eminently simple. Create a folder named "s" on the Lesson1_1 disk. Whenever a disk boots, AmigaDOS checks the folder named "s" to see if it contains a file called "startup-sequence". As it's empty at present, we need to create that file.

Open a new CLI window. Type the following (assuming the disk is in df0:):

ed df0:s/startup-sequence

This should create a startup-sequence file. Type

Pong

, then an asterisk, then confirm by pressing Y. This will save the new startup-sequence.

Congratulations: you have an autobootable disk.

Conclusion

We now have a disk that will autoboot and run an existing program. It's still a systems-friendly program, and can be run under Workbench. The common OS startup routines are still in place. But we can boot.

If you're anything like me, you may be underwhelmed at this point. "What? It's that easy?"

On the one hand... yes, yes it is. On the other, though, 'easy' was really the point of this exercise. Every journey has to begin by one single step.