Jekyll2024-01-06T20:07:25+00:00https://silverhammermba.github.io/blog/feed.xmlFloating little leaves of codeA blog is a regularly updated website or web page, typically one run by an individual or small group, that is written in an informal or conversational style.Maxwell AnselmStack Overflow is working as intended2024-01-06T00:00:00+00:002024-01-06T00:00:00+00:00https://silverhammermba.github.io/blog/2024/01/06/stack-overflow<p>I see Stack Overflow getting a lot of hate these days, especially from less experienced programmers.
The common complaint is that the site has a toxic culture that punishes and humiliates newbies, discouraging participation.
That is true, but that is also intended.
Weirdly, this is also what makes Stack Overflow such a useful site.
In this blog post, I will explain why that is as well as how to rake in those sweet, sweet fake Internet points on Stack Overflow.</p>
<h2 id="instructions-unclear">Instructions unclear</h2>
<p>The welcome page for Stack Overflow says:</p>
<blockquote>
<p>[This] is a question and answer site for professional and enthusiast programmers.</p>
</blockquote>
<p>So that means if you have a programming question, you should click the “Ask Question” button, right?</p>
<p><strong>WRONG.</strong></p>
<p>This misleading introduction is the primary cause of almost every complaint I have seen about Stack Overflow.
Yes the site is in the format of a Q&A and yes it is programming focused, but there are a million <em>completely valid</em> programming-related questions out there that will get you downvoted into oblivion.
What determines if a question will win you those Internet points or shame you into quitting programming?
The easiest way to know is to first understand what the site <em>really</em> is.</p>
<h2 id="the-true-goal">The true goal</h2>
<p>Here is what the welcome page <em>should</em> say:</p>
<blockquote>
<p>[This] is a database of every programming question that can have an objectively correct answer.
Every such question you could ask has <strong>already</strong> been asked on this site and every reasonable answer has <strong>already</strong> been given.
All questions and answers are already ranked according to their utility to the programming community at large.
If you have a question, simply search for the existing instance of it and upvote it, then upvote the answer you found most useful.
In the extremely unlikely event that such a question has not been asked or answered, you may attempt to do so.</p>
</blockquote>
<p>This explains almost all downvotes you will get on the site.
Let’s see why.</p>
<h3 id="valid-questions-that-get-downvoted-and-why">Valid questions that get downvoted and why</h3>
<p>“How do I learn ABC?” or “Which tool should I use for XYZ?” will <em>always</em> get downvoted.
These questions do not have objectively correct answers since they depend on the individual asking and their ultimate goals, which are both impossible to capture in the question.
Even if they could be answered, the “right” answer would likely change dramatically over time as programming evolves.
This runs counter to the site’s goal of being a definitive database of objectively ranked answers.</p>
<p>“What is the bug in this complete program? (Source code attached)” will <em>almost</em> always get downvoted.
It is <em>extremely</em> rare that this has an objectively correct answer.
Usually the expected behavior of the program is entirely unclear from the question and the reader is left to infer that from context clues.
This makes it hard to understand what the bug even is, so it gets downvoted.
Even if the expected behavior is clear, the design goals of the program itself are usually unknown.
Is this a toy project meant for learning?
Is this a throwaway utility that will be used once and never again?
Is this for enterprise software that needs to be understood and maintained years from now?
Does it need to run under specific memory/speed constraints?
<em>If</em> you manage to clear up all of those details in the question you probably won’t get downvoted.
If the resulting question ends up relatively short, you <em>might</em> get an answer and upvotes.
Answering these kinds of questions is extremely labor-intensive, so they often get ignored.</p>
<p>“How do I do [COMMON TASK] in [COMMON LANGUAGE]?” will <em>almost</em> always get downvoted; not because it’s a bad question, but because there is probably an existing instance of the question with answers!
Want to reverse a string in C#?
Want to invert a map in Javascript?
Great!
<strong>For the love of God do not ask on Stack Overflow.</strong>
First ask yourself, “Is it likely that a whole bunch of people in the history of programming have tried to do something like this before?”
If so, it’s <em>probably</em> already on Stack Overflow.
After all, that is the whole point of the site: to be a database of such questions!
If you care about imaginary Internet points, you are much better off spending 20 minutes rewording Google searches until you get a Stack Overflow result than asking a new question.</p>
<p>“How do I do [BIZARRE TASK] in [COMMON LANGUAGE]?” will <em>almost</em> always get downvoted.
These are questions like “How do I delete the fourth instance of the letter J from a string in Go?” or “How do I treat a UDP socket as if it’s a <code class="language-plaintext highlighter-rouge">void</code> heap pointer in C++?”
These questions may have objectively correct answers, but they make the reader question the asker’s sanity.
If it seems like no one else would ever benefit from such a question being answered, you get downvoted.
Such questions, even if objectively answered, would fill the database with noise, making it harder to use Stack Overflow for its intended purpose.
Usually these questions are posed by two kinds of people:</p>
<ol>
<li>Students who were given homework.
Homework contains these bizarre tasks specifically so that students cannot easily Google the answers.
Filling Stack Overflow with a database of every weird scenario a programming teacher cooked up would qualify as noise, hence the downvotes.</li>
<li>Someone suffering from <a href="https://xyproblem.info/">the XY problem</a>.
It is more likely that people will search for ways to do X (rather than the asker’s weird fixation on Y).
Thus Stack Overflow punishes questions about Y and rewards only those about X.</li>
</ol>
<p>Sometimes, more often than I’d like, you will get downvoted even for a question with objectively correct answers, which has never been asked before, which would be helpful to other people.
Why?
Because it’s the Internet and it’s full of lazy assholes with poor reading comprehension.
<em>Usually</em> they will leave a comment explaining why.
Don’t argue, just edit your question to address their comment (usually requires adding further clarification), and leave a comment @ing them saying their concern was addressed.
Though it may grate on your nerves, doing this is important because potential answerers often ignore questions with downvotes, so the extra effort maximizes your chance of a successful answer.
This also feeds into the site’s ultimate goal of becoming the definitive question database since placating the asshole likely resulted in your question becoming even more searchable and even less ambiguous such that any future prospective asker will stumble upon your question before asking.</p>
<h3 id="valid-answers-that-get-downvoted-and-why">Valid answers that get downvoted and why</h3>
<p>Answers that just link to some external site which has an answer will <em>always</em> get downvoted.
Remember, Stack Overflow wants to be the complete database itself, so external links are counter to that goal.
An external link could break, making what was once a valuable answer useless.
You can include an external link for extra information, but Stack Overflow always wants the answer to work on its own.</p>
<p>Answers that just include code with no explanation will <em>almost</em> always get downvoted.
Without an explanation of what the code does or why, it is hard to evaluate the quality of the answer.
Very simple self-evident code is sometimes okay.</p>
<h2 id="how-to-easily-get-points-on-stack-overflow">How to easily get points on Stack Overflow</h2>
<p>First, I do know what I’m talking about.
I currently have 21,453 points, 5 gold badges, and 50 silver badges on Stack Overflow.
So I’m not talking out of my ass.</p>
<p>Remember that it’s all about the database: improve the quality of the Q&A database and you get points.
Unintuitively, it’s not about asking a lot of questions or posting a lot of answers!</p>
<h3 id="fill-in-google-gaps">Fill in Google gaps</h3>
<p>If you make what seems like a reasonably clear Google search and get no Stack Overflow hits, and then spend 45 minutes trying different searches until you find your answer, that often indicates an opportunity for points.
Take your original Google search and ask it as a question!
Even if it gets closed as a duplicate, you will still rake in the points in the long run.
Why?
Because chances are someone else will make that reasonably clear Google search, see <em>your</em> question, see that it has answers, and upvote it!
This makes sense even if it gets closed as a duplicate because your more-easily-Googleable question improves the utility of the database!</p>
<p>A lot of the following tips also tie into this same mechanism: make the Google results for Stack Overflow better and you will be rewarded.</p>
<h3 id="chase-new-technology">Chase new technology</h3>
<p>The main situation where the database is missing questions and answers is for new tech that doesn’t have wide adoption.
If you’re an early adopter and need to figure out stuff on your own, ask those questions!
Even if you already figured out the answer, ask them anyway (remember that you can answer your own question)!
If the new technology takes off, you’ll start raking in points once the Google searches start hitting your questions.</p>
<p>This is actually not <em>that</em> easy because you’ll be racing against millions of other programmers trying to do the same thing.
But as the profession expands, you might be able to find a niche where you can be the first to ask.</p>
<h3 id="turn-comments-into-answers">Turn comments into answers</h3>
<p>For some reason, people love to answer questions in the comments.
This is bad for the database!
Comments have worse visibility and cannot be as easily ranked and voted on as answers.
It may seem cheap, but if you ever see someone answer in the comments, or a highly upvoted answer sucks but has better info in the comments, just steal it!
Consolidate the valuable comment info into a single, cohesive answer and you’re helping everyone else see that!
It’s better for the database, so you <em>will</em> get points for this.</p>
<h3 id="leave-clarifying-comments">Leave clarifying comments</h3>
<p>You don’t get many points for comments, but it can add up.
Highly voted answers get high traffic by definition, so see if you can improve them, even in little ways.
Is it missing a link to official documentation?
Add it in a comment.
Does a function have an optional parameter that wasn’t explained?
Leave a comment explaining the broader usage of it.</p>
<h3 id="answer-by-tag">Answer by tag</h3>
<p>I almost never look at the front page.
Technically you can get crazy points for answering there due to the high visibility but practically it’s extremely difficult.
Instead, try to find a few tags that you are very experienced with and follow them.
Remember to also set related tags which you know nothing about as “ignored”.</p>
<p>For example, I’m very good with Ruby but I know nothing about Ruby on Rails, so I follow the former and ignore the latter.
When I’m in the mood for trying to answer on Stack Overflow, I can click on the Ruby tag and I’m much more likely to see questions in my area of expertise.
I find this much easier to produce high quality content for the database rather than spreading myself thin across all of my areas of expertise.
If I see nothing interesting for Ruby, I’ll go down my list of followed tags.</p>
<p>By focusing narrowly for answers and comments, you’ll have a much higher chance of being first to respond.
Faster responses is valuable to the database, and also to you since duplicates are almost always downvoted.</p>
<h3 id="wait">Wait</h3>
<p>Remember that the points represent general utility to the programming community, not volume of work.
One of my single most successful days on Stack Overflow was when I posted an accepted answer which quickly racked up 11 upvotes.
That’s 135 points!
But it was a total quirk: the question was about a very esoteric topic and I think I just got lucky with a bunch of bored programmers browsing the site that day.
The question has had literally no activity since.</p>
<p>Don’t waste your time trying to hit highs like this.</p>
<p>Contrast that with a few answers I’ve left on highly voted questions (lots of Google hits) that simply fill in the gaps with more situational approaches not mentioned in the top answers.
Just two of these answers (which aren’t even accepted) are worth <em>over 2000 points</em>!
The questions are very commonly Googled, so even though my answers are more situational, by pure volume they happen to help out a lot more people!
These answers reliably get a couple upvotes a week.
By having enough answers like that out there and by waiting, you will eventually develop a passive income on Stack Overflow.</p>
<p>When you see someone with thousands of points like me, remember you’re not necessarily seeing thousands of hours of effort.
They could just have a few highly useful contributions.</p>
<h2 id="the-value-of-stack-overflow">The value of Stack Overflow</h2>
<p>All of the implicit rules certainly create a hostile environment for the clueless newbies attracted to Stack Overflow.
But they are also what make it such a precise, highly focused, Q&A machine.
That is ultimately the genius of Stack Overflow: if you try to game the system, you just end up making the database better.
That’s what all of my previous tips accomplish!</p>
<p>Even in this age of generative AI, we need Stack Overflow around to extract all of that human knowledge and to store it in a highly structured, searchable database so that the large language models can ingest it.
That is probably what 50% of ChatGPT-written-code is at the end of the day: a glorified Stack Overflow search.</p>
<p>Is it difficult and frustrating to ask and answer questions on Stack Overflow?
Absolutely.
I wouldn’t have it any other way.</p>Maxwell AnselmI see Stack Overflow getting a lot of hate these days, especially from less experienced programmers. The common complaint is that the site has a toxic culture that punishes and humiliates newbies, discouraging participation. That is true, but that is also intended. Weirdly, this is also what makes Stack Overflow such a useful site. In this blog post, I will explain why that is as well as how to rake in those sweet, sweet fake Internet points on Stack Overflow.A universal definition of string length2023-11-14T00:00:00+00:002023-11-14T00:00:00+00:00https://silverhammermba.github.io/blog/2023/11/14/string-length<p>There are many posts on the Internet discussing the complexity of computing
string length these days, but few making a clear recommendation of the right
thing to do. In some sense, that’s probably because there is no single right
answer.</p>
<p>But when it comes to free-form text (text which can contain non-English
characters, emojis, etc.), I would argue that there is one very good answer and
a bunch of crappy ones. So I’ll skip straight to giving you that good answer
now:</p>
<h2 id="the-one-good-universal-definition-of-free-form-string-length">The one good universal definition of free-form string length</h2>
<p>When represented as valid Unicode, you should count <strong>the number of <a href="https://www.unicode.org/glossary/#unicode_scalar_value">Unicode
scalar values</a></strong> in the string.</p>
<p>You may want to handle control characters in a special way, but we’ll get into
that later.</p>
<h2 id="why-do-you-need-a-universal-definition-of-string-length">Why do you need a universal definition of string length?</h2>
<p>If your software ingests strings that need to be saved somewhere, you should
care about string length. Remember that a string can contain the entire
collected works of Shakespeare. Think about what happens if all of your strings
are that size.</p>
<p>And unless you have the luxury of writing all of your software in a single
language, running on a single platform, with a single point of data entry, you
need to worry about how the same string can be represented in different ways
depending on the circumstances. If you have two pieces of software that can each
input a string that gets saved in the same database, you don’t want them
counting the length in different ways. Then you risk one piece of software being
able to load strings from the database that it won’t let the user write back to
it. That would be annoying.</p>
<p>So why is this not trivial? Because everyone wants to measure string length in
“characters”. But what is a “character” anyway?</p>
<ol>
<li>In most software you can count characters in a string by moving a cursor
through the string or trying to select portions of the string and seeing what
can/cannot be selected. What this <em>actually</em> counts is <a href="https://www.unicode.org/glossary/#grapheme_cluster">grapheme clusters</a>:
the “horizontally segmentable” parts of the string.</li>
<li>Each grapheme cluster is a sequence of 1 or more <a href="https://www.unicode.org/glossary/#grapheme">graphemes</a>. Annoyingly,
the Unicode standard calls these “characters”, which I am highly suspicious
of. Most modern software allows users to input grapheme clusters with a
single click or tap, and does not clearly subdivide them into graphemes.</li>
<li>Each grapheme is a sequence of 1 or more <a href="https://www.unicode.org/glossary/#code_point">code points</a>, which are the
things you can actually look up in the Unicode tables e.g., <code class="language-plaintext highlighter-rouge">U+1F4A9</code></li>
<li>Some code points (<a href="https://www.unicode.org/glossary/#surrogate_code_point">surrogates</a> and <a href="https://www.unicode.org/glossary/#noncharacter">noncharacters</a>) are used only in
certain encodings or for internal Unicode use and never represent characters
in Unicode. All other code points are <a href="https://www.unicode.org/glossary/#unicode_scalar_value">Unicode scalar values</a>.</li>
<li>Each Unicode scalar value is represented by 1-4 bytes (depending on the
encoding)</li>
</ol>
<p>So there are 5 different ways to count string length: grapheme clusters,
graphemes, code points, scalar values, and bytes. Why did I pick scalar values?
Anything above code points is a non-starter: graphemes and grapheme clusters
can contain an arbitrary number of code points and thus an arbitrary number of
bytes! A length limit is pointless if a single “character” can be arbitrarily
large. 3 and 5 are also no good because they are encoding-dependent and thus
not universal: strings usually have different lengths when encoded in UTF-8 and
UTF-16, and may contain different code points!</p>
<p>That’s why counting Unicode scalar values is the one good measure of string
length. The nice thing is that <em>most</em> characters in <em>most</em> languages are
represented with individual scalar values, so <em>most</em> of the time your users
won’t be too confused by this method of counting. They may just need to get used
to certain fancy characters like emojis consuming more of their string length
than others.</p>
<h2 id="control-codes">Control codes</h2>
<p><a href="https://www.unicode.org/charts/PDF/U0000.pdf">C0</a> and <a href="https://www.unicode.org/charts/PDF/U0080.pdf">C1</a> are portions of the Unicode table containing control codes.
Technically these are scalar values like any other and can be counted normally,
but if you are specifically handling user-created text as opposed to a terminal
user interface you probably don’t want them.</p>
<p>Most of them exist purely to support backwards compatibility with cryptic legacy
systems e.g., <code class="language-plaintext highlighter-rouge">U+009D</code> “operating system command”. Some are useful but have
better Unicode alternatives. For example the ambiguous “new line” which often
uses some combination of the control codes <code class="language-plaintext highlighter-rouge">U+000A</code> and <code class="language-plaintext highlighter-rouge">U+000D</code> but can be
better represented with <code class="language-plaintext highlighter-rouge">U+2028</code> “line separator” and <code class="language-plaintext highlighter-rouge">U+2029</code> “paragraph
separator”. In my applications, I simply strip control codes from the text and
calculate the string length after.</p>
<h2 id="how-to-count-unicode-scalar-values">How to count Unicode scalar values</h2>
<p>It is easy but often not obvious how to count scalar values. This happy family
will help us out: <code class="language-plaintext highlighter-rouge">👨👨👦👦</code>. This family contains 7 scalar
values (the right answer), but contains 1 grapheme cluster (on most modern
software), 11 code points in UTF-16, 22 bytes in UTF-16, and 25 bytes in UTF-8.</p>
<p>Here’s how to do it right (and wrong) in a bunch of languages. If you know how
to do this in a language I didn’t include here, please leave a comment!</p>
<h3 id="c">C#</h3>
<div class="language-c# highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">str</span><span class="p">.</span><span class="nf">EnumerateRunes</span><span class="p">().</span><span class="nf">Count</span><span class="p">()</span> <span class="c1">// 👍</span>
<span class="n">str</span><span class="p">.</span><span class="n">Length</span> <span class="c1">// ❌ number of UTF‑16 code points</span>
</code></pre></div></div>
<h3 id="go">Go</h3>
<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">len</span><span class="p">([]</span><span class="kt">rune</span><span class="p">(</span><span class="n">str</span><span class="p">))</span> <span class="c">// 👍</span>
<span class="nb">len</span><span class="p">(</span><span class="n">str</span><span class="p">)</span> <span class="c">// ❌ number of bytes when UTF‑8 encoded</span>
<span class="n">utf8</span><span class="o">.</span><span class="n">RuneCountInString</span><span class="p">(</span><span class="n">str</span><span class="p">)</span> <span class="c">// 👍 also works</span>
</code></pre></div></div>
<h3 id="javascript">JavaScript</h3>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[...</span><span class="nx">str</span><span class="p">].</span><span class="nx">length</span> <span class="c1">// 👍</span>
<span class="nx">str</span><span class="p">.</span><span class="nx">length</span> <span class="c1">// ❌ number of UTF‑16 code points</span>
</code></pre></div></div>
<h3 id="kotlin-jvm">Kotlin (JVM)</h3>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">str</span><span class="p">.</span><span class="nf">codePointCount</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="n">str</span><span class="p">.</span><span class="n">length</span><span class="p">)</span> <span class="c1">// 👍</span>
<span class="n">str</span><span class="p">.</span><span class="n">length</span> <span class="c1">// ❌ number of UTF‑16 code points</span>
</code></pre></div></div>
<h3 id="python">Python</h3>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">len</span><span class="p">(</span><span class="nb">str</span><span class="p">)</span> <span class="c1"># 👍
</span></code></pre></div></div>
<h3 id="ruby">Ruby</h3>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">str</span><span class="p">.</span><span class="nf">length</span> <span class="c1"># 👍</span>
</code></pre></div></div>
<h3 id="swift">Swift</h3>
<div class="language-swift highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">str</span><span class="o">.</span><span class="n">unicodeScalars</span><span class="o">.</span><span class="n">count</span> <span class="c1">// 👍</span>
<span class="n">str</span><span class="o">.</span><span class="n">count</span> <span class="c1">// ❌ number of grapheme clusters</span>
</code></pre></div></div>Maxwell AnselmThere are many posts on the Internet discussing the complexity of computing string length these days, but few making a clear recommendation of the right thing to do. In some sense, that’s probably because there is no single right answer.Android Versions2023-10-02T00:00:00+00:002023-10-02T00:00:00+00:00https://silverhammermba.github.io/blog/2023/10/02/android-versions<p>So you want to write Android apps? Well first you have to understand the
incredibly complicated world of versions in Android. Start by answering these
questions.</p>
<ol>
<li>What version of the <a href="https://kotlinlang.org/docs/releases.html">Kotlin language</a> do you want to write in? You
should definitely be writing in Kotlin. We’ll call this your <strong>Kotlin
version</strong>.</li>
<li>What version of the <a href="https://www.java.com/releases/">Java language</a> do you want to write in? You really
shouldn’t be writing Java but sometimes you gotta. Just the major version is
enough. We’ll call this your <strong>Java version</strong>.</li>
<li>What’s the oldest <a href="https://developer.android.com/tools/releases/platforms">Android</a> version you want your app to run on?
Great. I don’t care. Instead find the oldest <em>Android API level</em> you want
your app to run on (e.g. Android 13 is API level 33). We’ll call this your
<strong>minimum Android version</strong>.</li>
<li>What’s the newest Android version you’re actually going to test your app on?
Again, I don’t care. Get that version’s Android API level. That’s your
<strong>target Android version</strong>.</li>
</ol>
<h2 id="fill-in-the-basics">Fill in the basics</h2>
<h3 id="kotlin">Kotlin</h3>
<p>Use your <strong>Kotlin version</strong> as the version of the kotlin gradle plugin applied
in your build script:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">plugins</span> <span class="p">{</span>
<span class="nf">id</span><span class="p">(</span><span class="s">"org.jetbrains.kotlin.<...>"</span><span class="p">)</span> <span class="n">version</span> <span class="s">"whatever"</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge"><...></code> is probably gonna be <code class="language-plaintext highlighter-rouge">android</code> or <code class="language-plaintext highlighter-rouge">multiplatform</code> depending on how cool
you are.</p>
<p>A general word of warning: this is the <em>only</em> place the Kotlin version must
appear. Other Kotlin plugins/libraries may look like they share this version but
that is only a loose convention. If those plugins/libraries have bugs and need
to be patched they will intentionally diverge from the language version! Don’t
reuse the Kotlin version in your build script for other things.</p>
<h3 id="java">Java</h3>
<p>Use your <strong>Java version</strong> to fill in these parts of your gradle build script:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">android</span> <span class="p">{</span>
<span class="nf">compileOptions</span> <span class="p">{</span>
<span class="n">sourceCompatibility</span> <span class="p">=</span> <span class="nc">JavaVersion</span><span class="p">.</span><span class="nc">VERSION_WHATEVER</span>
<span class="n">targetCompatibility</span> <span class="p">=</span> <span class="nc">JavaVersion</span><span class="p">.</span><span class="nc">VERSION_WHATEVER</span>
<span class="p">}</span>
<span class="nf">kotlinOptions</span> <span class="p">{</span>
<span class="n">jvmTarget</span> <span class="p">=</span> <span class="s">"WHATEVER"</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I love how these are totally different types in the script.</p>
<p>These versions should always match! If you need to use a different Java version
for some of your code you need to split it into a separate gradle project so it
gets its own build script and can get its own versions.</p>
<p>Note that this only affects how you write and build your Java code. Because of
Android’s <a href="https://developer.android.com/studio/write/java8-support">desugaring</a> process, your app can still run on Android
versions with older JVMs. The compiler will let you know if you pick a version
that’s too new.</p>
<h3 id="android">Android</h3>
<p>Your <strong>minimum and target Android versions</strong> also go in the build script:</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">android</span> <span class="p">{</span>
<span class="nf">defaultConfig</span> <span class="p">{</span>
<span class="n">minSdk</span> <span class="p">=</span> <span class="mi">69</span>
<span class="n">targetSdk</span> <span class="p">=</span> <span class="mi">69</span> <span class="c1">// can be greater than minSdk</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="the-rest">The rest</h2>
<p>Now you need to pick a bunch of other versions, all constrained ultimately by
the answers you chose above.</p>
<h3 id="android-compile-sdk">Android Compile SDK</h3>
<p>When you compile your Android app, it will compile it against a certain version
of the API. This should be <strong>greater than or equal to your target Android
version</strong>. Technically you can always choose the latest API version, but
practically it never needs to be greater than your target version. Choosing a
larger version gives you access to the newer APIs earlier, letting you decouple
your development cycle slightly from Android’s release cycle.</p>
<p>Whatever version you choose, you must specify it in your gradle build script.
While you’re at it, you also get an easy one: you need build tools and those can
pretty much always be the latest version since they’re usually backwards
compatible, so just look up the latest <a href="https://developer.android.com/tools/releases/build-tools#notes">build tools</a> and specify it.
You can use older tools, but not older than the compile SDK version.</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">android</span> <span class="p">{</span>
<span class="n">compileSdk</span> <span class="p">=</span> <span class="mi">69</span> <span class="c1">// can be greater than targetSdk</span>
<span class="n">buildToolsVersion</span> <span class="p">=</span> <span class="s">"420"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Technically you can leave out build tools and one will be picked for you but
it’s a better idea to be explicit so you can easily pull in a patch if one is
released.</p>
<h3 id="android-gradle-plugin-agp">Android Gradle Plugin (AGP)</h3>
<p>If you’ve watched <a href="https://www.youtube.com/watch?v=e1BQeYlKOgA">my talk about gradle</a> you know that it isn’t so much
a build tool as it is a tool for making build tools. AGP is the <em>actual</em> build
tool for building Android apps. Your script needs to apply either the
<code class="language-plaintext highlighter-rouge">com.android.application</code> or <code class="language-plaintext highlighter-rouge">com.android.library</code> plugin. You want the latest
version of this plugin since newer versions can fix build issues and improve
build performance. Hopefully there isn’t a reason you can’t use the newest
version. Foreshadowing.</p>
<div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nf">plugins</span> <span class="p">{</span>
<span class="nf">id</span><span class="p">(</span><span class="s">"com.android.application"</span><span class="p">)</span> <span class="n">version</span> <span class="s">"whatever"</span>
<span class="p">}</span>
</code></pre></div></div>
<h3 id="gradle">Gradle</h3>
<p>Oh yeah, you needed gradle set up. Why are we only getting to it now? Because
you don’t care. You want the latest version of gradle so that your build runs
super fast and doesn’t have any compiler issues. Hopefully there isn’t a reason
you can’t use the newest version. Foreshadowing.</p>
<p>You should be using gradle wrapper and setting the version in
<code class="language-plaintext highlighter-rouge">gradle-wrapper.properties</code>:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>distributionUrl=https\://services.gradle.org/distributions/gradle-whatever-bin.zip
</code></pre></div></div>
<h3 id="android-studio">Android Studio</h3>
<p>Yeah you need Android Studio too. Again, latest version.</p>
<h2 id="dependency-hell">Dependency Hell</h2>
<p>And now all the foreshadowing pays off. Everything is broken! 💥</p>
<p>Well, maybe. Why? Because AGP, Gradle, Android Studio, Kotlin, and Java can all
be incompatible with each other. Yay! There’s no simple answer here. You have to
go back and start downgrading stuff until it starts working or, if you can’t
find a valid combination, change one of your original four answers to make it
possible. Good luck.</p>
<p>The most important things to keep consistent here are Kotlin, Java, AGP, and
your Android versions. If you get them wrong, you won’t necessarily see a
spectacular failure at build time! It may fail…</p>
<ul>
<li>at runtime</li>
<li>when running on a particular Android version</li>
<li>when building for release</li>
<li>any combination of the above</li>
</ul>
<p>Be very careful when changing the versions of <em>any</em> of these things!</p>
<p>Be sure to test your app thoroughly on your min and target Android versions. It
can break even when you didn’t change them!</p>
<p>The <a href="https://developer.android.com/build/releases/gradle-plugin">AGP release notes</a> are pretty good at capturing possible
incompabilities, but ultimately you just have to try it.</p>
<h2 id="more-dependency-hell">More dependency hell</h2>
<p>The story continues with every library you include. Want to grab the latest
version of some library? Well that might require you increasing your Java target
compatibility, which means you need to update Kotlin, which means you need to
update AGP, which means you need to update Gradle, which means… etc.</p>Maxwell AnselmSo you want to write Android apps? Well first you have to understand the incredibly complicated world of versions in Android. Start by answering these questions.Easy Animations in DragonRuby with Enumerators2023-02-08T00:00:00+00:002023-02-08T00:00:00+00:00https://silverhammermba.github.io/blog/2023/02/08/animation<p>I’ve always struggled with doing programmatic animations in game engines, and
DragonRuby is no exception. The result is always very math heavy, easy to screw
up, and hard to understand even after you get it working. Say you want to
animate a sprite moving back and forth with the following parts to the
animation:</p>
<ol>
<li>keep the sprite stationary on the left for 0.5 seconds</li>
<li>over the next 1 second, smoothly move the sprite to the right</li>
<li>keep the sprite stationary on the right for 0.5 seconds</li>
<li>over the next 1 second, smoothly move the sprite to the left</li>
<li>repeat</li>
</ol>
<p>Doing that using the built-in animation functions in DragonRuby involves adding
code like this to your <code class="language-plaintext highlighter-rouge">tick</code> method:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Numeric</span>
<span class="c1"># linear interpolate from start to finish as this number varies from 0 to 1</span>
<span class="k">def</span> <span class="nf">lerp</span> <span class="n">start</span><span class="p">,</span> <span class="n">finish</span>
<span class="p">((</span><span class="n">finish</span> <span class="o">-</span> <span class="n">start</span><span class="p">)</span> <span class="o">*</span> <span class="nb">self</span> <span class="o">+</span> <span class="n">start</span><span class="p">).</span><span class="nf">to_f</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="c1"># determine total animation length ahead of time</span>
<span class="n">total_duration</span> <span class="o">=</span> <span class="mi">3</span><span class="p">.</span><span class="nf">seconds</span>
<span class="c1"># mod the tick count so animation will repeat</span>
<span class="n">t</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">tick_count</span> <span class="o">%</span> <span class="n">total_duration</span>
<span class="c1"># first, guess that we're in the left-to-right part</span>
<span class="n">progress</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="nf">easing</span><span class="p">.</span><span class="nf">ease</span><span class="p">(</span>
<span class="mf">0.5</span><span class="p">.</span><span class="nf">seconds</span><span class="p">,</span> <span class="c1"># delay before this part of the animation</span>
<span class="n">t</span><span class="p">,</span> <span class="c1"># tick count (modified, since we're looping)</span>
<span class="mi">1</span><span class="p">.</span><span class="nf">seconds</span><span class="p">,</span> <span class="c1"># duration of this part of animation</span>
<span class="n">easing_func</span> <span class="c1"># a proc to smooth the animation (more on this later)</span>
<span class="p">)</span>
<span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">sprite_pos</span> <span class="o">=</span> <span class="n">progress</span><span class="p">.</span><span class="nf">lerp</span><span class="p">(</span><span class="mi">320</span><span class="p">,</span> <span class="mi">960</span><span class="p">)</span>
<span class="c1"># if that part is done, we must be in the right-to-left part</span>
<span class="k">if</span> <span class="n">progress</span> <span class="o">>=</span> <span class="mi">1</span>
<span class="n">progress</span> <span class="o">=</span> <span class="n">args</span><span class="p">.</span><span class="nf">easing</span><span class="p">.</span><span class="nf">ease</span><span class="p">(</span>
<span class="mi">2</span><span class="p">.</span><span class="nf">seconds</span><span class="p">,</span> <span class="c1"># delay before animation, taking into account the previous parts</span>
<span class="n">t</span><span class="p">,</span> <span class="c1"># remaining args same as before</span>
<span class="mi">1</span><span class="p">.</span><span class="nf">seconds</span><span class="p">,</span>
<span class="n">easing_func</span>
<span class="p">)</span>
<span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">sprite_pos</span> <span class="o">=</span> <span class="n">progress</span><span class="p">.</span><span class="nf">lerp</span><span class="p">(</span><span class="mi">960</span><span class="p">,</span> <span class="mi">320</span><span class="p">)</span>
<span class="k">end</span>
</code></pre></div></div>
<p>This sucks.</p>
<p>If you’ve never struggled with this stuff before, I’ll spell it out. It sucks
that we have to calculate the overall animation duration ahead of time; it sucks
that we have to calculate a cumulative delay for each additional part of the
animation; it sucks that we need an explicit mechanism for determining which
part of the animation we should be in; it sucks that each frame has to
re-compute parts of the animation that aren’t active; it sucks that this code
will get harder and harder to reason about as we add more phases to the
animation; and above all it sucks that our original human-readable description
of the animation is now completely hidden in the code. Good luck reading this
tomorrow and understanding how it effects the desired behavior.</p>
<p>But two months ago, I stumbled upon a <a href="https://michaelbitzos.com/devblog/programmatic-animation-using-coroutines">blog post</a> about how to solve
this sort of problem in Unity. I was curious if there’s an analogus way to apply
the technique to DragonRuby and I finally got the time to figure it out.</p>
<h2 id="enumerator">Enumerator</h2>
<p>First, a brief diversion into a very cool part of Ruby: enumerators. In Ruby we
have simple loops:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="nb">puts</span> <span class="n">i</span>
<span class="n">i</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">4</span>
<span class="k">break</span> <span class="k">if</span> <span class="n">i</span> <span class="o">></span> <span class="mi">100</span>
<span class="k">end</span>
<span class="c1"># outputs 0, 4, 16, 52</span>
</code></pre></div></div>
<p>But loops are all-or-nothing: once you start running them you have to wait for
them to finish… or do you? Enter <code class="language-plaintext highlighter-rouge">Enumerator</code>, with this you get fine-grained
lazy evaluation:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">i</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">e</span> <span class="o">=</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">yielder</span><span class="o">|</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="n">yielder</span> <span class="o"><<</span> <span class="n">i</span>
<span class="n">i</span> <span class="o">=</span> <span class="mi">3</span> <span class="o">*</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">4</span>
<span class="k">break</span> <span class="k">if</span> <span class="n">i</span> <span class="o">></span> <span class="mi">100</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="n">e</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># 0</span>
<span class="n">e</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># 4</span>
<span class="n">e</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># 16</span>
<span class="n">e</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># 52</span>
<span class="n">e</span><span class="p">.</span><span class="nf">next</span> <span class="c1"># raises StopIteration</span>
</code></pre></div></div>
<p>This is magic! It looks misleadingly simple because the Ruby interpreter is
doing some cool stuff under the hood for us. That <code class="language-plaintext highlighter-rouge">yielder << i</code> line is the
real key: that’s where the interpreter suspends execution of the block and saves
it to memory. The enumerator remains there, dormant, until we call <code class="language-plaintext highlighter-rouge">next</code> and it
briefly springs to life until the next <code class="language-plaintext highlighter-rouge">yielder << i</code> line is hit. The other key
part is that the enumerator is constructed with a block so, like all blocks, it
captures its local scope. That means the <code class="language-plaintext highlighter-rouge">i</code> variable remains accessible to it.
That would be the case even if we were to return the enumerator from this scope:
the local variable would be gone but the enumerator asleep on the heap would
still have a reference to it until it is done enumerating.</p>
<p>This is exactly what we need to solve our animation problem.</p>
<h2 id="eease">eease</h2>
<p>A simple tool:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">ecount</span> <span class="n">duration</span><span class="p">,</span> <span class="o">&</span><span class="n">blk</span>
<span class="n">enum</span> <span class="o">=</span> <span class="p">(</span><span class="mi">0</span><span class="o">...</span><span class="n">duration</span><span class="p">).</span><span class="nf">each</span>
<span class="k">return</span> <span class="n">enum</span> <span class="k">unless</span> <span class="n">blk</span>
<span class="n">enum</span><span class="p">.</span><span class="nf">lazy</span><span class="p">.</span><span class="nf">map</span> <span class="o">&</span><span class="n">blk</span>
<span class="k">end</span>
<span class="n">ecount</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span> <span class="c1"># construct an enumerator that yields 0..29</span>
<span class="n">ecount</span><span class="p">(</span><span class="mi">2</span><span class="p">.</span><span class="nf">seconds</span><span class="p">)</span> <span class="c1"># construct an enumerator that yields 0..119</span>
</code></pre></div></div>
<p>In DragonRuby, <code class="language-plaintext highlighter-rouge">x.seconds == x * 60</code> since it runs at a constant 60 FPS.
Maybe you see where this is going?</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1"># in tick</span>
<span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">animation</span> <span class="o">||=</span> <span class="n">ecount</span><span class="p">(</span><span class="mi">2</span><span class="p">.</span><span class="nf">seconds</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
<span class="nb">puts</span> <span class="s2">"Animation is on frame </span><span class="si">#{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span>
<span class="p">}</span>
<span class="k">begin</span>
<span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">animation</span><span class="p">.</span><span class="nf">next</span>
<span class="k">rescue</span> <span class="no">StopIteration</span>
<span class="c1"># animation is done</span>
<span class="k">end</span>
</code></pre></div></div>
<p>By calling <code class="language-plaintext highlighter-rouge">next</code> each frame, this enumerator now represents a real-time
animation (currently just “animating” some text into the console, but you get
the idea)! Already this solves a lot of our problems:</p>
<ul>
<li>We no longer need to externally manage the duration of the animation. It
remembers what duration it was initialized with and it tells us when it is
done (via <code class="language-plaintext highlighter-rouge">StopIteration</code>)</li>
<li>This doesn’t require precomputing an absolute start-time based on the tick
count. The animation starts whenever you first call <code class="language-plaintext highlighter-rouge">next</code></li>
<li>This no longer wastes time on previous stages of animation. Ruby pauses the
enumerator between frames and resumes it right where it left off</li>
<li>We can easily construct an array of enumerators to manage a large collection
of simultaneous animations, all running independently</li>
</ul>
<p>But we’re not done yet. Now that we can easily start independent animations,
DragonRuby’s convention of basing animations on tick counts is too restrictive.
Many animations don’t care how many tick counts came before they started or what
tick count it is now, they only care about how far along they should be in their
particular state change. So a new tool is needed:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">eease</span> <span class="n">duration</span><span class="p">,</span> <span class="n">easing_func</span><span class="p">,</span> <span class="o">&</span><span class="n">blk</span>
<span class="no">Enumerator</span><span class="p">.</span><span class="nf">new</span> <span class="k">do</span> <span class="o">|</span><span class="n">yielder</span><span class="o">|</span>
<span class="k">if</span> <span class="n">duration</span> <span class="o">==</span> <span class="mi">1</span>
<span class="n">yielder</span> <span class="o"><<</span> <span class="n">blk</span><span class="p">[</span><span class="n">easing_func</span><span class="p">[</span><span class="mf">1.0</span><span class="p">]]</span>
<span class="k">else</span>
<span class="n">last_i</span> <span class="o">=</span> <span class="p">(</span><span class="n">duration</span> <span class="o">-</span> <span class="mi">1</span><span class="p">).</span><span class="nf">to_f</span>
<span class="p">(</span><span class="mi">0</span><span class="o">...</span><span class="n">duration</span><span class="p">).</span><span class="nf">each</span> <span class="k">do</span> <span class="o">|</span><span class="n">i</span><span class="o">|</span>
<span class="n">yielder</span> <span class="o"><<</span> <span class="n">blk</span><span class="p">[</span><span class="n">easing_func</span><span class="p">[</span><span class="n">i</span> <span class="o">/</span> <span class="n">last_i</span><span class="p">]]</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>This is similar to <code class="language-plaintext highlighter-rouge">ecount</code> except we always yield a value from 0 to 1,
representing the progress through the animation, passed through an easing
function. An easing function is a function that takes an input from 0 to 1 and
outputs something from 0 to 1, such as <code class="language-plaintext highlighter-rouge">GTK::Easing.quad</code>. The idea is that
<code class="language-plaintext highlighter-rouge">eease</code> is the enumerator version of DragonRuby’s <code class="language-plaintext highlighter-rouge">ease</code>.</p>
<p>Instead of taking a start time it starts whenever you first call <code class="language-plaintext highlighter-rouge">next</code>, it
doesn’t need a tick count because it tracks that internally, it still gets a
duration and an easing function, and instead of returning its progress, it yields
it so you can encapsulate the entire animation in the construction of the
enumerator. Using it looks like this:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">animation</span> <span class="o">||=</span> <span class="n">eease</span><span class="p">(</span><span class="mi">2</span><span class="p">.</span><span class="nf">seconds</span><span class="p">,</span> <span class="no">GTK</span><span class="o">::</span><span class="no">Easing</span><span class="p">.</span><span class="nf">method</span><span class="p">(</span><span class="ss">:quad</span><span class="p">))</span> <span class="p">{</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
<span class="c1"># you'll see the progress slowly accelerate over time, due to Easing.quad</span>
<span class="nb">puts</span> <span class="s2">"Animation is </span><span class="si">#{</span><span class="p">(</span><span class="n">t</span> <span class="o">*</span> <span class="mi">100</span><span class="p">).</span><span class="nf">floor</span><span class="si">}</span><span class="s2">% complete"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We have what we need to define the individual pieces of our animation, but now
we want to wrap it up in one parent animation, with the parent mainly delegating
to the child animations, but still doing some extra work like setting up the
initial state and looping the whole thing. This will be handy:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">class</span> <span class="nc">Enumerator::Yielder</span>
<span class="k">def</span> <span class="nf">run</span> <span class="n">enum</span>
<span class="n">enum</span><span class="p">.</span><span class="nf">each</span> <span class="p">{</span> <span class="o">|</span><span class="n">v</span><span class="o">|</span> <span class="nb">self</span> <span class="o"><<</span> <span class="n">v</span> <span class="p">}</span>
<span class="k">end</span>
<span class="k">end</span>
</code></pre></div></div>
<p>Now a single enumerator’s yielder can go off on a side quest to run another
animation to completion before continuing on to the next stage of animation.
Combined with the built-in <a href="https://ruby-doc.org/3.2.0/Enumerator.html#method-i-2B">Enumerator::+</a> that concatenates enumerators
to run one after the other, finally we can rewrite our original animation:</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">animation</span> <span class="o">||=</span> <span class="no">Enumerator</span><span class="p">.</span><span class="nf">new</span> <span class="p">{</span> <span class="o">|</span><span class="n">yielder</span><span class="o">|</span>
<span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">sprite_pos</span> <span class="o">=</span> <span class="mi">320</span>
<span class="kp">loop</span> <span class="k">do</span>
<span class="n">yielder</span><span class="p">.</span><span class="nf">run</span><span class="p">(</span>
<span class="n">ecount</span><span class="p">(</span><span class="mf">0.5</span><span class="p">.</span><span class="nf">seconds</span><span class="p">)</span> <span class="o">+</span>
<span class="n">eease</span><span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="nf">seconds</span><span class="p">,</span> <span class="n">easing_func</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
<span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">sprite_pos</span> <span class="o">=</span> <span class="n">t</span><span class="p">.</span><span class="nf">lerp</span><span class="p">(</span><span class="mi">320</span><span class="p">,</span> <span class="mi">960</span><span class="p">)</span>
<span class="p">}</span> <span class="o">+</span>
<span class="n">ecount</span><span class="p">(</span><span class="mf">0.5</span><span class="p">.</span><span class="nf">seconds</span><span class="p">)</span> <span class="o">+</span>
<span class="n">eease</span><span class="p">(</span><span class="mi">1</span><span class="p">.</span><span class="nf">seconds</span><span class="p">,</span> <span class="n">easing_func</span><span class="p">)</span> <span class="p">{</span> <span class="o">|</span><span class="n">t</span><span class="o">|</span>
<span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">sprite_pos</span> <span class="o">=</span> <span class="n">t</span><span class="p">.</span><span class="nf">lerp</span><span class="p">(</span><span class="mi">960</span><span class="p">,</span> <span class="mi">320</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">)</span>
<span class="k">end</span>
<span class="p">}</span>
<span class="n">args</span><span class="p">.</span><span class="nf">state</span><span class="p">.</span><span class="nf">animation</span><span class="p">.</span><span class="nf">next</span>
</code></pre></div></div>
<p>This now reads very similarly to our original English description without
needing any comments! I haven’t even mentioned some of the bonus benefits yet.
Now that we can uniformly represent all of our animations as <code class="language-plaintext highlighter-rouge">Enumerator</code>
objects, we get fun new options like easily delaying frames (skip a call to
<code class="language-plaintext highlighter-rouge">next</code>) to simulate lag, easily skipping frames (call <code class="language-plaintext highlighter-rouge">next</code> twice) to speed up
animations after the fact, easily reset or replace animations while they are
running (reassign the variable that stores the animation), etc.</p>
<p>I’m still playing around with this pattern and exploring the possibilities, but I
would love to see something like this added to <a href="https://github.com/DragonRuby/dragonruby-game-toolkit-contrib">DragonRuby’s open source
module</a> at some point. I may do it myself.</p>
<p>But in the meantime, you can accomplish a lot with just <code class="language-plaintext highlighter-rouge">ecount</code>, <code class="language-plaintext highlighter-rouge">eease</code>, and
<code class="language-plaintext highlighter-rouge">Enumerator::Yielder::run</code>. All of the code in this blog post is licensed
<a href="https://creativecommons.org/publicdomain/zero/1.0/">CC0</a> so go forth and make stuff!</p>Maxwell AnselmI’ve always struggled with doing programmatic animations in game engines, and DragonRuby is no exception. The result is always very math heavy, easy to screw up, and hard to understand even after you get it working. Say you want to animate a sprite moving back and forth with the following parts to the animation:Gradle still sucks2023-01-06T00:00:00+00:002023-01-06T00:00:00+00:00https://silverhammermba.github.io/blog/2023/01/06/gradle<p>I wrote a blog post a while ago <a href="/blog/2022/02/16/gradle">complaining about Gradle</a>, mostly
from a position of ignorance: I had never really taken the time to properly
learn the tool and I was frustrated while trying to stumble my way through
working with it. But luckily, my company encourages learning and experimentation
so I was given an entire week–40 whole working hours–to do nothing but learn
Gradle. So I did. And I still hate it, but for different reasons.</p>
<h2 id="how-to-learn-gradle">How to learn Gradle</h2>
<p>During my period of intense frustration with Gradle I found this <a href="https://melix.github.io/blog/2021/01/the-problem-with-gradle.html">condescending
blog post</a> claiming that anyone who dislikes it is simply using it
wrong. So when I finally got the opportunity to learn it, I was adamant that I
would learn it the “right way”. No outdated tutorials, no hacks, no workarounds.
I wanted to learn only the modern, idiomatic, as-God-intended method of Gradle
usage.</p>
<p>To be fair, this is always the best way to learn any tool. Always be wary of
learning something through only practical examples, how-to guides, or
copy-pasted code. Because us programmers are universally terrible at everything
we do, and if you learn that way you will invariably end up learning someone
else’s bad practices. It is always better to go back to first principles; find
resources that zoom out and tell you what the tool is for, how it is generally
intended to be used, and the overall design patterns used with it. Ideally only
trust sources very closely associated with the development of the tool itself
since second- and third-hand sources can accidentally introduce their own
baggage.</p>
<p>Having learned idiomatic Gradle, I can say that almost none of the resources out
in the wild are good. The good list is extremely short:</p>
<ul>
<li><a href="https://www.youtube.com/playlist?list=PLWQK2ZdV4Yl2k2OmC_gsjDpdIBTN0qqkE">Understanding Gradle</a> is a great video series walking through most of the
basic pieces of gradle and what they are for. It doesn’t have enough detail to
teach you how to get a fully idiomatic build, but at least it gives you a
sense of what kind of stuff you should be doing.</li>
<li>The official docs on <a href="https://docs.gradle.org/release-nightly/userguide/structuring_software_products.html">structuring large projects</a> presents similar
information in a text form. I object to the word “large” in the title since it
makes it sound exceptional in some way, as if most gradle projects won’t
benefit from this approach. I think a more accurate title would be
“structuring nontrivial projects”. If you’re doing some toy project in gradle,
sure you don’t need this. But likely any serious project should be aware of
these practices.</li>
<li>The <a href="https://github.com/jjohannes/idiomatic-gradle">idiomatic Gradle</a> repo is a complex project applying all of the best
practices. Not super useful when you’re first learning because it will look
like alien hieroglyphs, but once you start to grok Gradle, it provides lots of
interesting examples to consider.</li>
</ul>
<p>Probably the most surprising discovery for me was just how useless most of the
official Gradle docs are. Many of them are outdated, incomplete, or so
specialized that they’re useless for general learning.</p>
<h2 id="what-is-gradle">What is Gradle?</h2>
<p>I won’t try to fully explain Gradle, that’s what the links above are for. But I
can tell you the one reason you are probably failing to understand Gradle. First
off, pretty much all of the <a href="https://www.bruceeckel.com/2021/01/02/the-problem-with-gradle/">common reasons</a> given online are wrong.
Well, not exactly wrong, but rather they are misleadingly simple.</p>
<p>Here is why you actually don’t get it: Gradle is <em>not</em> a build tool, rather it
is <strong>a platform for building build tools</strong>. As soon as you stop seeing it as a
simple means of hooking together tasks and dependencies you’ll start seeing why
it is so complex.</p>
<h2 id="why-does-it-suck">Why does it suck?</h2>
<p>I love this quote by Peter Bhat Harkins:</p>
<blockquote>
<p>One of the most irritating things programmers do regularly is feel so good
about learning a hard thing that they don’t look for ways to make it easy, or
even oppose things that would do so.</p>
</blockquote>
<p>I almost fell for this trap. Once I started actually solving problems with
Gradle I noticed that I started talking about its complexities in a more
positive light. But that’s stupid. When I took a step back I realized that all
of the problems I had with Gradle before still existed, I just understood how to
use them to get work done. But the fundamental problem with Gradle is that it
simply does not justify these complexities.</p>
<h3 id="a-case-study">A case study</h3>
<p>To learn Gradle, I set myself a task of incorporating some custom code
generation into our build process. This is the kind of thing I can easily do
with shell scripting, but I wanted it to be a proper part of Gradle since it
perfectly fits the traditional build-tool model of monitoring when an input
file changes to determine when to run a task that generates an output file.</p>
<p>At a high level, the process is this:</p>
<ol>
<li>Run some command line stuff to prepare the code generation tools</li>
<li>Run a script on some input files to produce intermediate metadata needed for
code generation</li>
<li>Run a script on the intermediate metadata to produce the code</li>
<li>Compile the code normally</li>
</ol>
<p>The goal is that I can run a single build task and it checks if either the
original input files or metadata have changed and reruns whatever parts of the
process are needed before building my code.</p>
<p>Could I have accomplished this just by opening up <code class="language-plaintext highlighter-rouge">build.gradle</code> and writing a
bunch of custom tasks? Absolutely. But doing it that way would be like writing
your entire program logic inside of <code class="language-plaintext highlighter-rouge">main</code>: technically feasible, but not
idiomatic.</p>
<p>So here is what I had to do in order to do it the right way:</p>
<ol>
<li>Since the part of the project relating to input files and metadata is
logically separate from the rest of our app, split it into a Gradle
subproject to follow best practices</li>
<li>Now that we have separate projects, best practice dictates that each has its
own “convention plugin” to distinguish the type of build it is performing, so
define a sub-build for storing custom plugins</li>
<li>Develop a custom plugin for the metadata generation. This involved going down
a huge rabbit hole of trying to use an open source community plugin since
technically some of the code generation stuff I was using could be run on the
JVM and thus could be built into gradle</li>
<li>Find out that the community plugin had a critical bug in one of my required
use-cases so I had to throw it out and start over. Develop a complete custom
Gradle plugin for running the command line steps I had been doing manually
before</li>
<li>Best practice is to define custom task types in your convention plugin rather
than customizing built-in tasks, so subclass gradle’s Exec task to specify
the kind of inputs and outputs needed for each command</li>
<li>Work around the fact that Gradle’s built-in Exec task doesn’t work on Windows</li>
<li>Best practice is to not tightly couple your build script to your plugin’s
tasks, so develop a mini DSL extension so that the tasks can change
independently of the build script</li>
<li>Now that the convention plugin is so complex, write end-to-end tests that
create mini gradle builds that apply the plugin and verify that the task
output is correct</li>
<li>Write another convention plugin for turning the metadata into source code</li>
<li>Break your brain trying to figure out how to apply Kotlin plugins to your
convention plugin while applying different versions of the same plugins to
your actual app build since Gradle bundles its own internal version of
Kotlin which is older than the one for your app</li>
<li>Create the code generation task. This is actually the easiest part since you
can pretty much write normal code with normal tests, though you still need
to figure out the right input/output types for the task since the best
practice is that all such types in Gradle are redirected through “provider”
types instead of being used directly</li>
<li>Write another mini DSL extension for customizing the code generation task</li>
<li>
<p>Figure out how to add generated code to a source set via the Kotlin Gradle
plugin. Duh, it’s obviously</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>kotlin {
sourceSets {
val commonMain by getting {
kotlin {
srcDir(...)
}
}
}
}
</code></pre></div> </div>
</li>
<li>Finally start setting up the actual Gradle builds. Apply the first
convention plugin and configure the custom metadata-producing tasks. Easy.
Wait. Shit. How do you get the output of the subproject task to be visible
to the other project so that Gradle understands the task dependency between
the two projects?</li>
<li>Define a “consumable configuration” in the subproject and add the task
output to it by declaring an “artifact”. Oops now your build script is
tightly coupled to the plugin task. This seems unidiomatic and unavoidable.</li>
<li>Remember to use <code class="language-plaintext highlighter-rouge">flatMap</code> on the task to get its output because task
creation is lazy!</li>
<li>Apply the other convention plugin where code generation is needed. Configure
the code generation task to get… wait, how do I get the artifact that I
declared in the subproject?</li>
<li>Define a “resolvable configuration” here. Declare a dependency which points
the resolvable configuration at the consumable configuration from the
subproject</li>
<li>
<p>Wonder what the hell a configuration is and why I’m declaring them. Check
the docs:</p>
<blockquote>
<p>A configuration is a named set of dependencies grouped together for a
specific goal.</p>
</blockquote>
<p>Yep that clarifies nothing</p>
</li>
<li>Hook up the resolvable configuration to the code generation task. Wait, it
needs a file, not a configuration. Arbitrarily grab the first file from the
configuration (since we know the subproject only added one file). That feels
shitty</li>
<li>Remember to use <code class="language-plaintext highlighter-rouge">map</code> on the configuration since it’s lazy!</li>
</ol>
<p>And, believe it or not, it’s as simple as that.</p>
<h3 id="tldr">tl;dr</h3>
<p>What sucks about the above process is not that it’s long. It’s the sheer number
of concepts that must be learned and interacted with: subbuilds, subprojects,
plugins, tasks, providers, extensions, source sets, configurations, artifacts,
dependencies, <em>and</em> all of the quirks and workarounds involved with each of
those things.</p>
<p>All of this complexity makes working with Gradle a slog. You can fully, deeply
understand every single aspect of your build until you want to do one thing
slightly differently and suddenly it doesn’t work because you were supposed to
be using some totally different part of Gradle you never heard about before.</p>
<p>The whole idea of using community-developed plugins becomes a minefield because
the chance of them applying all of the concepts completely correctly is
essentially 0%, meaning you might be okay as long as you’re only doing basic
stuff, but as soon as you stray off the beaten path stuff starts breaking and
you have almost no chance to fix it properly yourself.</p>
<p>The complexity does come with some small benefits. It’s cool that our custom
Gradle plugins have their own tests so we can independently test our code
generation process without touching any of our actual app code. Once you
understand how Gradle likes to structure things, it does make your build scripts
less cluttered and each bit of build logic ends up compartmentalized in a
sensible place.</p>
<p>But goddam it feels like there must be an easier way to accomplish this stuff.
I’ve learned dozens of different tools over my software development career and
compared to them the amount of comfort I feel with Gradle after 40 hours of
dedicated study is atrociously low. I have since gone on to further enhance our
Gradle build process and it honestly hasn’t felt any easier. The only difference
from before is that when working on Gradle stuff I used to spend way more time
than I expected, get incredibly frustrated, and give up; now I spend way more
time than I expected, get incredibly frustrated, and eventually (somehow) find a
fix.</p>Maxwell AnselmI wrote a blog post a while ago complaining about Gradle, mostly from a position of ignorance: I had never really taken the time to properly learn the tool and I was frustrated while trying to stumble my way through working with it. But luckily, my company encourages learning and experimentation so I was given an entire week–40 whole working hours–to do nothing but learn Gradle. So I did. And I still hate it, but for different reasons.Required features in your new programming language2022-07-22T00:00:00+00:002022-07-22T00:00:00+00:00https://silverhammermba.github.io/blog/2022/07/22/language<p>Making cool new programming languages is all the rage these days. I honestly
love this since it shows that people are still trying to think about new ways
for humans interact with computers at a low level. However, many of the
designers of these new languages don’t seem to realize that the bar for language
designs is higher than it used to be. We’re way past the limited designs of
established languages like C, C++, and Java; we know that programming languages
can be much more helpful, safe, and expressive than those older languages and it
saddens me to see someone make a brand new language with the same artificial
limitations that we know of.</p>
<p>I’m not saying that we shouldn’t use C, C++, and Java anymore, I’m saying that
those languages already exist! Why make a new language if it doesn’t materially
improve on the status quo? So here is a list of features that I consider to be
essential for a new language if you want it to be taken seriously. In my opinion
the only good reason to omit any of these in your new language is</p>
<ul>
<li>you’re making a toy language just for fun, or</li>
<li>you’re making a niche language that you don’t intend people to use for
general-purpose programming</li>
</ul>
<h3 id="project-structure--directory-structure">Project structure == directory structure</h3>
<p>In older languages, you can throw your source code files basically anywhere
because in order to compile those files into a working program, you need some
metadata separate from those files which explains how they should be combined.
That metadata could be some kind of project definition file, explicitly listing
the source files and how they relate to each other, or the metadata could be
some kind of build script that explicitly describes how the compiler will be
invoked and with which files.</p>
<p>This is insanity. I have never seen a project that actually <em>requires</em> this
degree of flexibility. Practically, every language involving this becomes a
headache to manage because you have to manually keep the metadata in sync at all
times, and you get a whole host of novel bugs simply related to the metadata
being wrong as opposed to the actual source code.</p>
<p>Any new language should have a single, standard project structure. That project
structure must be directly expressed through the directory structure of the
source code. If a project metadata file is allowed, at most it can specify a
custom location for that directory structure; it cannot do things like including
extra source files from nonstandard locations, or exempting source files which
are in standard locations. The goal is that anyone should be able to view the
directory structure of a project and immediately understand roughly how that
project works.</p>
<h3 id="package-manager">Package manager</h3>
<p>We live in the age of Open Source. There is no excuse for copy-pasting other
people’s code into your project anymore. Any new language should have a built-in
official package manager with the following features:</p>
<ul>
<li>Creating, publishing, and downloading packages</li>
<li>License information is required when publishing and included when downloading</li>
<li>Projects list package dependencies with their required versions</li>
<li>Installed packages are pinned to a specific version until explicitly updated
in the project</li>
<li>All dependencies of a project (including transitive) can be audited (including
version and license)</li>
</ul>
<h3 id="language-manager">Language manager</h3>
<p>If your language is new, it will have bugs. You will need to release new
versions, those versions may introduce incompatibilities. Any new language
should have an official language version manager such that each project can pin
itself to a specific language version, and the manager can download whatever
version is required prior to building.</p>
<p>It should be possible to simultaneously build two projects using two different
language versions on the same machine i.e. the language version is sandboxed to
the individual build, as opposed to some global configuration of the machine.</p>
<h3 id="turing-incomplete-builds">Turing-incomplete builds</h3>
<p>Going hand-in-hand with the previous points, any new compiled language should
have a single, standard compilation process. Every step of that process may
include a finite set of options for tweaking it, however no step should allow
custom code to be run as part of the build.</p>
<p>Again, the goal is that simply by looking at the project structure I should have
an intuitive understanding of how those files will be built. It should not be
possible for someone to define a custom, wacky build process that works entirely
differently. Debugging the build process should at most involve walking through
the standard set of steps and determining which ones are going wrong, as opposed
to debugging completely custom build scripts written in a Turing-complete
language.</p>
<p>If you want to include custom build steps, those must be externally defined
per-project and must run before the standard build starts. If the users of your
language start to regularly step outside of the standard build process because
it is too restrictive, the build process should be expanded just enough to
handle those use-cases <em>without</em> giving in and allowing Turing-complete builds.
For example, if users of your language start using custom code generators,
consider making code generation an official part of your language such that it
can be standardized.</p>
<h3 id="mixed-paradigm">Mixed paradigm</h3>
<p>Purity of paradigm is worthless. Pure functional languages like Haskell remain
extremely niche despite years of advocacy. Virtually all Java successors hack
top-level functions into the JVM since we know they are simply the best option
in some cases.</p>
<p>Any new language should be mixed paradigm, allowing at least the creation of
mutable objects encapsulating both data and methods, as well as first-class
functions that can exist independent of an enclosing object and which can be
stored and passed around like an object. 99% of good software design is about
accurately modeling the problem you are solving. Purity in a language serves
only to restrict your models, forcing you to use unintuitive representations in
some cases.</p>
<h3 id="everything-really-is-an-object">Everything really is an object</h3>
<p>Since your language must be mixed paradigm, for programmer sanity you should
ensure that everything in your language behaves like an object and can have
methods attached to it. Yes, that means strings, integers, classes, functions,
etc. should all allow methods to be defined on them. There should never be cases
where something in a variable is of some exceptional type that has no class, or
cannot accept method calls. Methods should be definable as scoped extensions to
existing classes, so that you can add methods to objects that you do not have
control over (if you think it makes more sense than defining a function separate
from the object).</p>
<p>Conversely, everything that <em>looks</em> like a method call should be a method call.
Constructors should not be a special case, they should be a method call on the
class object. Calling a top-level function should be syntactic sugar for a
method call on the function object e.g. <code class="language-plaintext highlighter-rouge">.invoke()</code>.</p>
<p>If your language is compiled, it should similarly allow interfaces to apply to
all of these constructs. If you want to define a class that works like a
function, you should be able to make that class and function conform to the same
interface such that they can be used interchangeably.</p>
<p>Although I am demanding this, I do acknowledge that this is an extremely
difficult requirement for any language design. In fact I don’t know of any
language that does this perfectly, since you often end up with increasingly
abstract levels of design logic that are hard to reason about. However, any new
language should strive to achieve this as much as is practically possible and
ensure that any remaining corner cases are well-understood and seem to be
acceptable compromises with easy workarounds.</p>
<h3 id="testing-framework">Testing framework</h3>
<p>Automated tests are essential for software quality. Any new language should have
an official built-in testing framework that supports automated unit and
integration tests. Test code should be part of the standard project structure
with a clear relationship with the source code. If the users of your language
start to regularly rely on nonstandard testing tools, you should expand the
official test framework to handle those use-cases.</p>
<p>Test output should have the option of being human-readable or being in a
standard machine-parsable format such that CI tools can integrate with the
language.</p>
<h3 id="regex">Regex</h3>
<p>Any new language should have first-class support for regular expressions both as
a built-in type and a literal in source code. If statically typed, regex
literals should be evaluated for correctness at compile time.</p>
<h3 id="pick-one-dynamic-or-inferred">Pick one: dynamic or inferred</h3>
<p>Any new language must run in one of these two ways:</p>
<ol>
<li><em>Fully dynamic</em>, meaning the language has an <code class="language-plaintext highlighter-rouge">eval()</code> function that accepts a
string at runtime and evaluates it as source code. The language is fully
metaprogrammable, allowing all programmer actions to be executed at
runtime.</li>
<li>Statically type-inferred, meaning that by default all types are checked at
compile time and can be specified in just enough source locations that the
compiler can verify through inference that types are being used correctly.
Runtime type-checking (such as downcasting) is allowed, but must always be
explicitly differentiated from static checking in the source code</li>
</ol>
<h3 id="sum-types">Sum types</h3>
<p>If your language is statically typed, it must have first-class support for <a href="https://en.wikipedia.org/wiki/Tagged_union">sum
types</a> as well as pattern matching for those types to ensure that all cases
of the type are handled whenever one is used.</p>
<p>Nullability in the language must be represented as a sum type with two cases:
null and non-null. The compiler should be able to enforce that either a variable
can never contain null or it can contain null and must be handled with pattern
matching.</p>
<h3 id="explicit-exceptions">Explicit exceptions</h3>
<p>If your language is statically typed, any function that throws an exception must
have a distinct type from any function that does not throw. Calling a function
that can throw must either require pattern matching to handle the exception or
making the caller throw.</p>
<h3 id="explicit-mutability">Explicit mutability</h3>
<p>If your language is statically typed, variables and types must be explicitly
marked as mutable or immutable. The compiler verifies that immutable variables
are never reassigned and that objects with an immutable type are never modified.</p>
<p>Ideally, immutable types can be given value semantics, meaning that objects of
that type with the same value are considered to be identical.</p>
<h2 id="the-future">The future</h2>
<p>There’s always room for improvement, and while I don’t think we’ve figured out
the following areas enough to say that they are essential to language design, I
would love to see new languages play around with them to find some new ideal.</p>
<h3 id="explicit-side-effects">Explicit side-effects</h3>
<p>If your language is statically typed, any function/method that modifies one of
its inputs or changes some global state of the program should be explicitly
marked as causing side-effects (distinguishing them from “pure”
functions/methods). The compiler should enforce that pure functions cannot call
impure ones.</p>
<h3 id="async-programming">Async programming</h3>
<p>It has been interested watching asynchronous programming evolve over the years.
We’ve gone from explicit multithreading, to green threads, to callback hell, and
now to modern concepts like coroutines. It is hard to say where this will settle
down, but my gut tells me we haven’t reached the end of this journey. Ideally we
don’t end up in <a href="https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/">colored function</a> hell.</p>Maxwell AnselmMaking cool new programming languages is all the rage these days. I honestly love this since it shows that people are still trying to think about new ways for humans interact with computers at a low level. However, many of the designers of these new languages don’t seem to realize that the bar for language designs is higher than it used to be. We’re way past the limited designs of established languages like C, C++, and Java; we know that programming languages can be much more helpful, safe, and expressive than those older languages and it saddens me to see someone make a brand new language with the same artificial limitations that we know of.Every programmer should care about UI design2022-07-10T00:00:00+00:002022-07-10T00:00:00+00:00https://silverhammermba.github.io/blog/2022/07/10/ui<p>A common view among programmers is that UI/UX design is thankfully not any of
our business. We have dedicated specialists for that who figure it all out so
that we never have to think about it. Unfortunately, this viewpoint is
incorrect. Why? As a programmer you do UI design almost every day whether you
like it or not.</p>
<p>You disagree? Perhaps you don’t realize who the users are that your interface is
for: the other programmers working on your code.</p>
<p>There’s a joke that any code that isn’t yours is spaghetti code. Part of the
problem is that it’s generally harder to read code than it is to write it, but I
think another major reason is that most programmers don’t realize that their
code is a UI, and when they don’t treat it like a UI they end up with a shitty
one.</p>
<p>Everyone can recognize bad UI when they use it, and working on bad code has
the same problems:</p>
<ul>
<li>Isn’t it frustrating when you have to click through 6 different things to get
to the one part of the app you actually care about? Isn’t it frustrating when
you have to include a bunch of boilerplate in order to make small functional
changes to the code?</li>
<li>Isn’t it frustrating when a screen is absolutely covered in information, yet
the one thing you want to find isn’t there and it’s not clear how to get to
it? Isn’t it frustrating when an object you want to use has 10 public methods,
but only 3 of them are useful and if you call them in the wrong order your
program crashes?</li>
<li>Isn’t it frustrating when you input a bunch of info into an app then an error
occurs and it throws it all away? Isn’t it frustrating when your code is super
permissive when compiling but then throws tons of runtime errors instead?</li>
<li>Isn’t it frustrating when an app labels its UI with meaningless buzzwords like
“My Solutions”, “Insights”, etc. making it needlessly difficult to learn how
to use it? Isn’t it frustrating when you have to maintain code with names like
<code class="language-plaintext highlighter-rouge">EntityOperationManager</code> and you have absolutely no idea what it is for
because each of those words has several different meanings and none of them
are specific?</li>
<li>Isn’t it frustrating when a trivial app takes 10 or 20 seconds to load? Isn’t
it frustrating when it takes 10 minutes for your simple app to build so you
can test your changes?</li>
</ul>
<p>The similarities between traditional UIs and code are not limited to their
users, they also apply to their design processes. Any UI/UX designer worth their
salt will tell you that there’s only one way to develop a good UI: test it.
<em>Really</em> test it. It’s not enough to ask people “How do you think this UI should
work?” It’s not enough to show people pictures of a mockup and ask if they like
it. No, you need real users to actually sit down in front of the UI and try to
use it.</p>
<p>And yet many software developers I’ve worked with seem to think that they can
just dream up a software design on a whiteboard and it will be perfect. Or they
use the software design to develop a working feature themselves, so it must be a
good software design. But did you test it? Not automated unit tests, did you
test the <em>UI</em> of your software design? Did you show it to other developers who
were unfamiliar with it and see if they could figure it out? Did you consider
all of the different kinds of users who will be interacting with it: the
developer bolting on a brand new feature in 3 months who had no hand in the
software design, the developer working at 3AM to track down a crash that is
somehow related to your changes, the newbie trying to learn the structure of
your code so that they can make good design decisions themselves?</p>
<p>Did you give some thought for those people—your future users—when you were
creating your software UI? Or did you make selfish decisions by making your
software hard to interact with?</p>
<h2 id="how-not-to-design-a-better-software-ui">How not to design a better software-UI</h2>
<p>Making a software design with a good UI is similar in many ways to designing a
good API for a library or web service: you need to think hard about things like
data formats, stability, understandability, naming; but this is also a trap.
There are other unique aspects to making a good software-UI, and you ignore them
at your peril.</p>
<p>They key difference is that while an API is often used by complete strangers who
you will never meet, the UI of your software design affects mainly your
teammates. It doesn’t need to be nearly as stable, it can (and often must)
evolve as the needs of your team/company/product evolve. It can also rely on
esoteric domain-specific knowledge, or impose harsh restrictions based on your
business needs.</p>
<p>The most frequent mistake I see when developers try to make a good software-UI
is that they start thinking about it as an API. “How would we design this if it
was an open source project used by thousands of people on github?” This often
immediately starts straying into <a href="https://martinfowler.com/bliki/Yagni.html">YAGNI</a> territory like, “We can’t make
that a hard dependency! What if we need to reuse this component in an
environment where the dependency isn’t available?” or “We can’t enforce that at
compile time! What if one day we need to reuse this component in a server that
needs to reconfigure its components while running?” Avoid the temptation to
optimize for flexibility above all else: your goal is to deliver value to your
company. Saving on future development cost by having a flexible software design
<em>is</em> one way to do that, but it isn’t the only way.</p>
<p>You can deliver much more immediate value by optimizing your design for clarity
of understanding, simplicity of use, early and informative error handling;
basically, all of the parts of the software design related to how other
developers will interact with your code right now, not based on hypothetical
unknown use-cases.</p>
<h2 id="a-better-approach-to-design">A better approach to design</h2>
<p>Rather than starting my software designs with an application programming
interface (API), I instead like to start developing them with a <em>programmer
interface</em> (PI). In other words, don’t try to start by laying out data types and
interfaces, instead start by writing the code as if your software design is
already done. Imagine you are another developer using your finished software
design and try to implement some of your expected use cases.</p>
<p>Let’s call this PI-driven-design, and like test-driven-development it can be a
little hard to wrap your head around it at first. Just like how TDD starts by
writing failing tests before the implementation is done, you start your PIDD by
writing code that can’t compile or run because none of the software exists yet.
The advantage of this is that it immediately strips away a lot of the complexity
that a whiteboard software design introduces. Rather than creating an explosion
of different types and interfaces and data structures and algorithms and design
patterns, you focus only on the immediate needs of the users of your software
design. Is the code readable? Is the intent of the code obvious by looking at
the names and types? Do the initialized objects do enough to seem useful without
encapsulating too much functionality? How will errors manifest if the code is
wrong: compiler error, exception, silent failure? Will the cause of the error be
clear to the programmer?</p>
<p>You can still think about flexibility, but again focus on how your code will
flex to accommodate other programmers as opposed to purely hypothetical use
cases. Is the code logically organized such that it is easy to find things later
if changes are needed? Is functionality encapsulated in a way that small
functional changes can be implemented easily? Let’s discuss two concrete
examples to illustrate what too much or too little flexibility looks like in
terms of a programmer interface.</p>
<h3 id="1-flexibility-über-alles">1. Flexibility über alles</h3>
<p>I have met a lot of programmers who, when adding a property to a class, always
add public getters and setters for it. In most cases I would consider this
user-unfriendly and a bad PI. The only “benefit” is that it provides the maximum
amount of flexibility: anything with access to that object can read it or modify
it at any time. But this also makes it much harder for programmers to use: any
code modification around that property might require refactoring in many other
parts of the code, any new interaction with that property might introduced
regressions in other code which was already using it. A much more user-friendly
choice is to do the opposite: make it completely private. Even better make it
private and immutable so that it can only be set once even inside the instance
(if your language supports that). This is user-friendly because it is the
simplest behavior to understand: if you are working outside of that class you
don’t have to think about it at all (encapsulation!), and if you are working
inside the class you only have to consider the value it was initialized with
(side-effect-free!). Not every software design works with such restrictive
properties, but you should fight tooth and nail to give up as little ground as
possible. If it must be public, at least make it immutable. Or if it must be
mutable, make it only publicly readable not writable.</p>
<p>Making your software design extremely flexible and robust often makes it more
verbose and challenging to use and reason about. It is wasteful if that
flexibility is based only on personal speculation. You’re spending resources
optimizing for a hypothetical situation while ignoring the immediate extra costs
you are incurring by making your software harder to use. Most likely your
software design needs a programmer interface which hides the flexibility and
directly addresses your immediate needs.</p>
<h3 id="2-useless-ease-of-use">2. Useless ease-of-use</h3>
<p>I have also met a lot of programmers who frequently use the singleton pattern.
When I ask why they are using singletons in their design they usually answer,
“This makes it very easy to use my code,” or “We only need one instance of this
object.” These justifications sound like they are in line with my previous
arguments: taking into account ease of use is very considerate of our users and
the singleton pattern is restrictive about instance creation so we’re also
favoring simplicity over flexibility!</p>
<p>However, I believe these arguments are twisting the truth. In OOP, singletons
are the exception, not the rule, it takes additional work to make a singleton in
most languages, and most singleton implementations demand some amount of special
treatment when it comes to their use and testing. While those previous arguments
are not necessarily false, they omit the key fact that those supposedly
PI-friendly benefits come with tradeoffs. To evaluate whether they are truly
good in terms of the PI, we must evaluate those tradeoffs.</p>
<p>In terms of ease of use, while singletons are definitely easy to use in
implementation they are much harder to use when it comes to testing. In some
languages they cannot be mocked at all if you aren’t already using dependency
injection, and even when they can be mocked they often require special mocking,
which makes it harder to write tests. If you listen to other industry experts,
you should have much more test code than application code, so this aspect
sounds like it is likely more negative than positive.</p>
<p>How about flexibility? The flexibility argument ignores that while the classic
singleton pattern does restrict initialization, it <strong>vastly</strong> expands access to
shared state. Normally if some code initializes an object, it is nontrivial to
give unrelated modules of code access to that same object, but not so with
singletons. With singletons you open up the possibility that any two areas of
code with access to the singleton’s namespace can now have a direct dependency
connecting them. Not only that, but this dependency can be an example of tight
coupling since often uses of a singleton assume that it is a singleton and thus
rely on the fact that the state is shared. Breaking this assumption can be
nontrivial. Thus, while slightly restricting flexibility, using a singleton can
introduce a huge amount of potential complexity by making it easy to tightly
couple your future code in ways that will be difficult to undo. Once again this
aspect sounds more negative in terms of the PI design.</p>
<p>The PI-friendly way to view the singleton pattern (and any other design pattern
for that matter) is that it is a potential <em>burden</em> on the design. It should
only be included if the PI specifically calls for it and it doesn’t have harmful
side-effects. As an example, let’s consider the PIDD for a logging feature for
an application. We want it to be really easy to call, something like</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>log.error("something went wrong")
</code></pre></div></div>
<p>and I want to be able to write that same code in any file and have it work the
same way. Most importantly, I want all of those log messages to end up in the
same place so that I get a unified log file for the whole application. This is a
PI challenge: I need other programmers to easily access that <code class="language-plaintext highlighter-rouge">log</code> object and I
want them to be able to easily understand that all logs will be unified. The
singleton pattern does solve these problems, but most importantly the tradeoffs
it introduces here seem negligible. Most applications don’t bother mocking their
logging interface in tests, in fact, you often use the real logs during testing
to help with debugging. And the only case in which we wouldn’t want the shared
mutable state of logging is if we wanted logs from different components to end
up in completely different log files, which seems like a very unlikely
hypothetical.</p>
<p>Unfortunately, I have rarely seen this kind of evaluation occur when deciding
what to include in a software design.</p>
<h2 id="practical-use">Practical use</h2>
<p>Practically, using PIDD in your software designs doesn’t require any major
changes to your development process. Usually I use it behind the scenes to
inform my software designs and to justify design decisions. So while I still
include the usual UML diagrams and flowcharts and such since those are useful
tools for communicating about the design, they are not what I start with. I
start with PIDD in the form of a very rough proof-of-concept, iterating on that
several times until I find something that feels great to interact with as a
programmer. Only afterwards do I start on my formal software design. But that’s
just how I do it, probably there are other viable ways to make your designs
programmer-interface-centric.</p>
<p>The important thing is that when you design your software, start with the users:
the people on your team! How will they use the software? Remember that in this
context “use” means: reading the code, extending the code, bug fixing the code.</p>Maxwell AnselmA common view among programmers is that UI/UX design is thankfully not any of our business. We have dedicated specialists for that who figure it all out so that we never have to think about it. Unfortunately, this viewpoint is incorrect. Why? As a programmer you do UI design almost every day whether you like it or not.A Paradoxical Error2022-05-31T00:00:00+00:002022-05-31T00:00:00+00:00https://silverhammermba.github.io/blog/2022/05/31/child-paradox<script type="text/javascript" async="" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
<p>I recently read <a href="https://www.amazon.com/GameTek/dp/1460757378">GameTek</a> by Geoffrey Engelstein, which includes several
interesting examples of how to improve your intuition when it comes to the
design and the playing of games. Unfortunately, two of these examples are at
best <em>extremely misleading</em> and at worst completely incorrect. However the way
in which they are wrong is fascinating and prompted several hours of thought and
discussion on my part, so I’d like to share them with you.</p>
<h2 id="the-premises">The Premises</h2>
<p>Engelstein presents two similar examples in the book, which I will reproduce
here. First, at the end of Chapter 6, “Feeling the Loss”, we have a presentation
of a classic paradox in probability called The Two Child Paradox.</p>
<blockquote>
<p>You’re talking to woman at a park and she say she has two children. Suddenly a
boy runs up and grabs her hand and you ask if this is her son. She replies
that he is.</p>
<p>What are the chances that her other child is a boy?</p>
<p>Most people say 50 per cent: the fact that one is a boy has no bearing on the
gender of the other child. But this is not correct. There are four possible
family arrangements that have two children: boy/boy, boy/girl, girl/boy and
girl/girl. When I tell you that at least one is a boy, that eliminates one
possible family: girl/girl. There are three combinations left: boy/boy,
boy/girl, and girl/boy. Each is equally likely, but in only one out of three
(and thus a ⅓ chance) is the other child a boy.</p>
</blockquote>
<p>Then at the end of Chapter 14, “Tic-Tac-Toe and Entangled Pairs”, we have this
other situation which might sound different but is actually intimately connected
to the first.</p>
<blockquote>
<p>Let’s say four people are playing <em>Bridge</em>. One of them says, ‘I have an Ace,’
and we know she is telling the truth. The chance that she’s holding more than
one Ace is about 37 per cent. Later the same player says, again truthfully, ‘I
have the Ace of Spades.’ Strangely, the chance that she has more than one Ace
is now 56 per cent.</p>
</blockquote>
<p>Take a moment to ponder these two examples. They probably feel unintuitive, but
all you need to do at this point is understand the premise of each one. When
you’re ready, proceed.</p>
<h2 id="the-paradox">The Paradox</h2>
<p>I don’t know about you, but I find the first example extremely unintuitive, if
not totally wrong. In my view, the key point that Engelstein omits is that the
children are <em>distinguishable</em>. Since you have met one of the children at the
park, you can imagine labeling that as the “park” child and the other one as the
“home” child. Going through the possible configurations we have:</p>
\[\text{boy}_{\text{park}}/\text{boy}_{\text{home}},\text{boy}_{\text{park}}/\text{girl}_{\text{home}},\text{girl}_{\text{park}}/\text{boy}_{\text{home}},\text{girl}_{\text{park}}/\text{girl}_{\text{home}}\]
<p>By discovering that the child in the park is a boy, we are not left with three
possibilities as Engelstein argues, but only two, one of which involves two
boys. So in my view the intuitive answer of 50% is actually correct. Engelstein’s
summary of “at least one is a boy” clashes with his earlier statement that we
learned that a specific child is a boy.</p>
<p>So why did Engelstein think it was 1 in 3? Is there even a paradox here? First
we should restate the example to remove some ambiguitiy. Let’s imagine that you
and I are playing a game with the following steps:</p>
<ol>
<li>I flip two fair coins and then hide the coins behind a screen. The random
result is fixed but only I can see it</li>
<li>I may reveal some information about the coins to you</li>
<li>I reveal both coins and you win if both coins are Heads</li>
</ol>
<p>Now consider how your chance of winning changes throughout the steps of the
game. After step 1 your chance will always be the same: two fair coins were
flipped, so there are four possibilities, so there is a 1 in 4 chance that both
are Heads.</p>
<p>HH, HT, TH, TT</p>
<p>Suppose in step 2 that I look at the coins and tell you that at least one of
them is Heads. What is your chance of winning? This is the situation that
Engelstein was trying for earlier: this is equivalent to me saying that it is
not the case that both coins are Tails; in other words, I have only eliminated
one of the four possibilities. So by Engelstein’s argument, you have a 1 in 3
chance of winning.</p>
<p>Instead, suppose that in step 2 I pick up one coin from behind the screen and
show you that it is Heads. In this case, we can apply <em>my</em> previous argument:
the coins are distinguishable (we have the revealed coin and the hidden coin)
thus there are only two remaining possibilities and so the chance of winning is
1 in 2.</p>
\[\text{H}_{\text{revealed}}/\text{H}_{\text{hidden}},\text{H}_{\text{revealed}}/\text{T}_{\text{hidden}},\color{red}{\text{T}_{\text{revealed}}/\text{H}_{\text{hidden}},\text{T}_{\text{revealed}}/\text{T}_{\text{hidden}}}\]
<p>Now the paradox: why is there a difference between these situations? In the
first case, where at least one coin was Heads, certainly I could have also
picked up a coin and showed you that it was Heads. Why does it matter if I
actually show you or not? It’s as if, by introducing completely redundant
information (physically showing you a Head rather than saying it), your chances
of winning are magically increased!</p>
<p>Here is where the two examples from earlier connect. In the Bridge example, I
first say “I have an Ace”, then by introducing completely redundant information
and saying it’s specifically the Ace of Spades, the chances of having more than
one Ace magically increase! Why does the redundant information help? Certainly
when I say I have an Ace you can imagine me showing you that it is one of the
specific Aces.</p>
<p>So what’s going on? Is Engelstein’s argument correct, or is mine? Or is
something else going on entirely?</p>
<h2 id="the-game">The Game</h2>
<p>To help you understand how to resolve this, we need to put some money on the
line. Now you have to pay to play, but there is a prize for winning:</p>
<ol>
<li>I flip two fair coins and then hide the coins behind a screen. The random
result is fixed but only I can see it</li>
<li>I may reveal some information about the coins to you and state both the prize
for winning $W and the cost of entry $C</li>
<li>If you pay, I reveal both coins and you win if both coins are Heads</li>
</ol>
<p>The question is if the game is worth playing; based on the information I
revealed, the prize money, and the cost of entry, do you expect to make money by
playing? For example, if you can calculate that the probability of two Heads
based on the information revealed is 50%, the prize is $24, and the cost of
playing is $10, then</p>
\[\text{expected winnings}=P(\text{win})\times($24-$10)-P(\text{lose})\times($10)=0.5\times $24-$10=$2\]
<p>In other words, you expect to make $2 per play on average (assuming you have a
big enough budget to play the game repeatedly and overcome some early bad luck).
As you can see from this example, it is crucial that you are able to calculate
the probability of winning in order to make a profit in this game. So imagine
you find me at the carnival offering that exact situation: the prize is $24, the
cost $10, and I’m showing you that a coin is Heads. Do you play?</p>
<p>Assuming you found my argument from earlier convincing, your chances of winning
are 1 in 2. Based on the previous calculation, you will make a profit! So you
play and indeed you win! So you keep playing, following the exact same strategy:
when I show you Heads you pay and, to keep things simple, when I don’t show you
Heads you decline to pay and simply wait for me to flip the coins again. With
this strategy you make a small but reliable profit ($2 per game on average);
you’re going to bleed me dry if it takes all day!</p>
<p>Then something changes. Suddenly your strategy starts losing. Convinced it must
simply be an unlucky streak, you play on, but eventually I completely drain your
funds and you walk away with empty pockets. What happened?</p>
<p>Imagine you’re in my shoes and you need to run this game and you’ll quickly see
what is missing from the picture: when do you decide to show the player a coin?
For example, I could follow a procedure like this:</p>
<ol>
<li>Flip the coins and place one on the left and one on the right</li>
<li>If the left coin is Heads, show them that coin</li>
<li>Otherwise, show them nothing.</li>
</ol>
<p>In this case, the 1 in 2 calculation is correct: whenever I show you a coin, the
only remaining possibilities are</p>
\[\text{H}_{\text{left}}/\text{H}_{\text{right}},\text{H}_{\text{left}}/\text{T}_{\text{right}}\]
<p>So playing is always profitable.</p>
<p>But what if instead I follow this procedure:</p>
<ol>
<li>Flip the coins and place one on the left and one on the right</li>
<li>If the left coin is Heads, show them that coin</li>
<li>Otherwise, if the right coin is Heads, show them that coin</li>
<li>Otherwise, show them nothing.</li>
</ol>
<p>Now whenever I show you a coin, it could be</p>
\[\text{H}_{\text{left}}/\text{H}_{\text{right}},\text{H}_{\text{left}}/\text{T}_{\text{right}},\text{T}_{\text{left}}/\text{H}_{\text{right}}\]
<p>and you have no way to distinguish between them. The chance of winning has
dropped to 1 in 3. Unfortunately, from your perspective, both procedures look
nearly identical. The only difference would be the frequency of me showing
nothing, but this is complicated by the fact that I can switch between these
procedures at will. If I start with the first procedure I can sucker you in by
making you think the game is rigged in your favor. When I switch strategies,
your expected winnings flip to −$2 and hopefully for me your greed will cost you
all of your profits and then some. I could even be sneakier and switch between
these strategies randomly, slightly favoring the one that pays me more.</p>
<p>If you approach this game naively, you will always lose in the long run. Why?
Because, like Engelstein, you will assume that the specific way in which you
were given information reveals something additional about the nature of <em>why</em>
that information was revealed; specifically, that me showing Heads versus saying
“There is at least one Heads,” implies something about the choice <em>I</em> made. In
general, there may be no connection between these things.</p>
<p>So this is the resolution of the paradox: it is <em>not</em> the case that revealing
redundant information can change the chances of an event, rather it is possible
to phrase a probability problem in an ambiguous way such that most people will
make seemingly obvious (but unsupported) assumptions about it and thus come to
incorrect conclusions.</p>
<h2 id="the-resolution">The Resolution</h2>
<p>Going back to the original Two Child Paradox. What were the unsupported
assumptions we made in order to arrive at the paradox in the first place?</p>
<p>To arrive at the answer of 1 in 2 for the chances of the other child being a
boy, we must assume that the gender of the child we met has no bearing on why we
met them at the park. If that same child were a girl, we are assuming they would
have still been brought to the park. This makes the gender irrelevant, since if
they brought a boy, we have boy/girl or boy/boy (1 in 2) and if they brought a
girl we have girl/boy or girl/girl (1 in 2). This seems like a reasonable
assumption to me, however it <em>is not stated</em> in the problem.</p>
<p>To arrive at the answer of 1 in 3, we must assume some strange parental
behavior, namely that only boys are brought to parks. In that case, the fact
that we have met a boy at a park means that this could be a family with two
boys, or boy/girl, or girl/boy since all of these equally likely scenarios would
result in us meeting a boy. Again this seems like a strange assumption, but it
is also not stated and thus cannot be excluded.</p>
<p>Back to Bridge. How do you arrive at the probabilities that Engelstein gave? If
I say “I have an Ace” what is the probability that I have two or more Aces? In
order to answer this we need to state our assumptions, namely, why did I say I
have an Ace? If we assume that whenever I have any Aces I always say “I have an
Ace” then indeed you arrive at about 37% chance of me having two or more. This
seems like a reasonable assumption to make.</p>
<p>But what if I say that I have the Ace of Spades? How do we arrive at 56% chance
of two or more Spades? That is the probability we get if we assume that I <em>only</em>
tell you when I have the Ace of Spades; specifically, if I have other Aces and
no Ace of Spades <em>I say nothing</em>. Similar to the Two Child Paradox this feels
like a very strange assumption to make. If we’re interested in finding Aces,
why wouldn’t I tell you about the others when I have them? This means that there
would be situations where I have two or more Aces (none of them Spades) but I
don’t reveal any information. A much more reasonable assumption here is if we
assume that when I have <em>any</em> Aces I tell you the suit of one of them. It’s not
hard to see that now I will tell you about an Ace (and its suit) whenever I have
any Aces, in other words we’re back in the first situation. So now even though I
am revealing additional information (the suit), the probability of having two or
more Aces is still 37%, which is the intuitive result.</p>
<h2 id="my-conclusion">My Conclusion</h2>
<p>If you only remember one thing from this, it is to be careful in your
assumptions when turning word problems into math problems since they can
completely change your answer. Engelstein mostly does a good job in his book of
debunking common false assumptions such as the idea of “hot streaks” in
gambling, but he ironically talks himself back into such bad assumptions with
these two examples. Both answers he gives are unintuitive because they make
unintuitive assumptions. If you make the more natural assumptions—in my
opinion—you get the intuitive answers and no paradox appears.</p>Maxwell AnselmSoftware Designs2022-04-13T00:00:00+00:002022-04-13T00:00:00+00:00https://silverhammermba.github.io/blog/2022/04/13/plans<blockquote>
<p>No plan of operations extends with any certainty beyond the first encounter
with the main enemy forces.</p>
<p><em>Prussian Field Marshal Helmuth von Moltke the Elder</em></p>
</blockquote>
<p>This is often paraphrased as “no plan survives contact with the enemy”. Wise
words.</p>
<p>I’d like to propose a variant of this saying: no software design survives first
contact with the compiler. Don’t spend too much time whiteboarding, writing UML,
etc. Because as soon as you see how the code actually works, how it actually
feels to test it and run it, there is no certainty that your software design
will continue to make sense without modification.</p>Maxwell AnselmNo plan of operations extends with any certainty beyond the first encounter with the main enemy forces. Prussian Field Marshal Helmuth von Moltke the ElderThoughts on DragonRuby Game Toolkit2022-02-19T00:00:00+00:002022-02-19T00:00:00+00:00https://silverhammermba.github.io/blog/2022/02/19/dragonruby<p>I have always been curious about the <a href="https://dragonruby.org/toolkit/game">DragonRuby Game Toolkit</a> (mainly
because I ❤️ Ruby) but since game dev is only a hobby of mine and there are
already enough free engines out there for me to tinker with, I never bothered to
shell out the $50 for the standard version. Luckily I was recently able to grab
a free copy as part of a game jam promotion, so I’ve finally been able to give
it a spin. So this is a review/summary of the engine from a hobbyists
perspective.</p>
<h2 id="what-is-it">What is it?</h2>
<p>DragonRuby describes itself as “a professional grade 2D game engine”, which is
true, but I think too vague. The same could be said of Unity and Godot (ignoring
their optional 3D modes), but DragonRuby could hardly be more different from
them. If you’ve ever made a game with just C and SDL or just C++ and SFML,
DragonRuby will feel much more familiar to you, because these engines primarily
concern themselves with</p>
<ul>
<li>Being cross-platform</li>
<li>Handling input</li>
<li>Drawing sprites/text on the screen</li>
<li>Playing audio files</li>
</ul>
<p>…and that’s basically it. Elements that you might call <em>very useful</em> in game
development (if not essential) such as vector math, animations, physics, a
camera, a scene editor, a UI system etc. are more or less completely absent. In
their place it has all of the precursor elements of these features. For example,
no animations but it understands how to slice up sprite sheets, no vectors but
functions exists for some common geometric calculations, no UI but it has
functions for positioning draw calls relative to the screen dimensions.
Basically you’re looking at reinventing these sorts of components if your game
needs them.</p>
<p>There is an unaffiliated open source package system called <a href="https://smaug.dev/packages/">Smaug</a> but
currently it sits at a measly 23 packages and most of those seem to be related
to <a href="https://github.com/guitsaru/draco">Draco</a>, an implementation of ECS in DragonRuby (which seems a little
odd… why not use Unity/Godot then?).</p>
<p>DragonRuby’s philosophy seems to be minimalism. Unlike Unity, which prescribes a
very specific structure for how your game should work, DragonRuby tries hard to
stay out of your way, which I respect. If you want to make a very simple, or
very nontraditional game, this kind of minimalism gives you a nice blank slate
to start from. It also results in some <a href="https://youtu.be/MFR-dvsllA4">impressive performance advantages</a>
over Unity.</p>
<p>All of that makes DragonRuby sound a bit unremarkable, but there is more to it.
Much more.</p>
<h2 id="dragonrubys-killer-features">DragonRuby’s killer features</h2>
<p>The main distinction between DragonRuby and SDL/SFML is the language: Ruby. This
isn’t just an aesthetic difference, this actually unlocks the amazing, unique
features of DragonRuby.</p>
<p>Probably the biggest feature that DragonRuby has over other engines is that it
has a built-in console. Since Ruby is a dynamic scripting language, this console
isn’t restricted to some specific subset of functionality, it uses the very
language that the rest of your game is written in and has access to the exact
same features! The console ends up operating like Unity’s editor on steroids:
not only can you examine and tweak the data in your game while it runs, you can
call arbitrary functions, write entirely new functions, and add custom debugging
tools to your game. It’s such a huge paradigm shift in game development that I
suspect I have yet to scratch the surface of the possibilities it unlocks. I bet
you could even write an entire game from within the game itself, using only the
console. Not sure why you would, but that shows how powerful this feature is.</p>
<p>Coupled with the console is DragonRuby’s hot-reload feature. Whenever you save a
source file, DragonRuby picks up the differences and almost instantly
incorporates them into the running game. This quick feedback makes iteration so
much faster than in other compiler-based engines.</p>
<p>Underlying all of this is DragonRuby’s <code class="language-plaintext highlighter-rouge">args</code>. This object essentially represent
the global state of the entire engine, not only all inputs and outputs, but
<em>all</em> game state (stored in <code class="language-plaintext highlighter-rouge">args.state</code>). This object persists in the engine as
long as you have it running, independent of the console and hot-reloading. This
is what ties the whole DragonRuby experience together. You can dynamically add a
new object to your game’s state in the console, then update your script to
interact with that object, then switch back to the console to perform further
tweaks, etc. It also has built-in serialization support.</p>
<p>And thanks to Ruby’s beautiful design, interacting with all of this is fairly
intuitive. <code class="language-plaintext highlighter-rouge">args</code> more or less operates as a normal Ruby object, with all of the
magic of the engine tucked away under the hood. It’s very easy to start simple
and add abstractions as-needed, mixing functional, imperative, and object
oriented styles to suit the structure of your game.</p>
<p>Less essential but still very cool is that it has a built-in demo recorder that
runs in-engine. This makes it easier to test things like asset swaps to see how
they impact your game (without needing to drive yourself crazy playing your game
over and over again).</p>
<p>All of these features put DragonRuby in a interesting position counter to
mainstream engines like Unity: sure it’s missing a lot of the basics, but it
also has these amazing fundamental features that would be <em>extremely</em> difficult
to pull off in other engines.</p>
<h2 id="the-bad-parts">The bad parts</h2>
<p>There are aspects I dislike, however.</p>
<p>The biggest one is that all of the dynamism that I was lauding earlier can be a
double-edged sword. Shared mutable state is probably the #1 cause of
sanity-destroying bugs in my experience and DragonRuby is essentially built
around it. Even when working on a simple game, you might find that the amazingly
fast iteration cycle frequently grinds to a halt when something weird starts
happening and nothing you do seems to fix it… until you restart the engine.
Yep, something you typed in console, something that got hot-reloaded, or some
interaction between the two put something weird into <code class="language-plaintext highlighter-rouge">args</code> and now it’s in
there until you either figure out what it was and undo it or kill the engine.</p>
<p>Second, you might think that an advantage of DragonRuby is that all of the
resources of the Ruby community are at your disposal. But for (totally valid)
technical reasons, DragonRuby actually runs on a fork of <a href="https://github.com/mruby/mruby">mRuby</a> so I
expect that many popular Ruby libraries are actually incompatible with the
engine. I wish this is something Smaug would handle: some kind of vetting system
for finding existing gems which are DragonRuby-compatible.</p>
<p>Third, the API design has issues. It provides too many ways to do the same
thing with too little explanation of why. For example you can draw on the screen
using an array, a hash, or an object. From what I’ve read in the docs, using a
hash is more performant and using an object gives you more control over
rendering order and using an array is for… what? Also how much more performant
is a hash over an object? Should I only introduce objects when absolutely
necessary or is the difference usually not that big? There is basically no
guidance about this stuff, so you just need to try it and see what
happens. Also, despite being in Ruby, the API design is <em>very</em> C-reminiscent.
For example <code class="language-plaintext highlighter-rouge">blendmode_enum: 1</code> means “alpha blending”; why not <code class="language-plaintext highlighter-rouge">blend_mode:
:alpha</code>? I’m pretty sure that’s nearly identical performance-wise and about 1000
times more readable.</p>
<p>Lastly, and perhaps most annoyingly, the documentation sucks. It’s one huge HTML
file with minimal organization. For example, where do you think you go to find
the explanation of how the aforementioned <code class="language-plaintext highlighter-rouge">blendmode_enum</code> works? “Rendering A
Sprite”? No. “More Sprite Properties As An Array” (subtitle: “Here are all the
properties you can set on a sprite.”)? No. How about under
<code class="language-plaintext highlighter-rouge">args.outputs.sprites</code>? No. It’s under “Different Sprite Representations”, of
course! It also has gaps. For example, several examples show objects calling the
method <code class="language-plaintext highlighter-rouge">lines</code> which I cannot find mentioned anywhere outside of source code. By
poking around in console (Ruby FTW) I figured out that it’s being included in
<code class="language-plaintext highlighter-rouge">Array</code> from the <code class="language-plaintext highlighter-rouge">GTK::Primitive::ConversionCapabilities</code> module but it’s still
a mystery to me what it’s for.</p>
<p>Also most of the docs are source code dumps of example games. The fact that so
many examples are included is great but it doesn’t do much at all in the way of
helping you understand them; you’re left to reverse engineer the techniques from
largely undocumented code. It also makes it a huge pain to Ctrl-F in the docs,
since you’ll get stuck paging through hundreds of lines of undocumented source
code rather than the single explanation of your search term.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Overall I actually really like DragonRuby. The documentation issues are
annoying, but the extent of functionality of the engine is so limited that it
isn’t possible to get extremely lost in it. And while its flexibility can bite
you, that flexibility does come with huge advantages, and it is also in line
with its theme of minimalism. If you want design your own, more restrictive
framework within DragonRuby, you are free to do so (e.g. Draco with ECS) and it
completely stays out of your way.</p>
<p>If the idea of having a bare-bones engine to build on top of appeals to you and
you don’t want to get stuck with the restrictiveness of C/C++, the $50 price tag
is definitely worth it. I’m intrigued by the features you get with higher-tier
licenses but those require an annual subscription and I still haven’t made a
dime off of game development.</p>Maxwell AnselmI have always been curious about the DragonRuby Game Toolkit (mainly because I ❤️ Ruby) but since game dev is only a hobby of mine and there are already enough free engines out there for me to tinker with, I never bothered to shell out the $50 for the standard version. Luckily I was recently able to grab a free copy as part of a game jam promotion, so I’ve finally been able to give it a spin. So this is a review/summary of the engine from a hobbyists perspective.