<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-CA"><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="https://eddieantonio.ca/blog/feed.xml" rel="self" type="application/atom+xml" /><link href="https://eddieantonio.ca/blog/" rel="alternate" type="text/html" hreflang="en-CA" /><updated>2023-10-26T23:03:54+00:00</updated><id>https://eddieantonio.ca/blog/feed.xml</id><title type="html">eddieantonio/blog</title><subtitle>My thoughts on software, linguistics, and jazz fusion.
</subtitle><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><entry><title type="html">Python is a Compiled Language</title><link href="https://eddieantonio.ca/blog/2023/10/25/python-is-a-compiled-language/" rel="alternate" type="text/html" title="Python is a Compiled Language" /><published>2023-10-25T00:00:00+00:00</published><updated>2023-10-25T00:00:00+00:00</updated><id>https://eddieantonio.ca/blog/2023/10/25/python-is-a-compiled-language</id><content type="html" xml:base="https://eddieantonio.ca/blog/2023/10/25/python-is-a-compiled-language/">&lt;p&gt;This blog post hopes to convince you that Python is a compiled language.
And by “Python”, I don’t mean alternate versions of Python like
&lt;a href=&quot;https://www.pypy.org/&quot;&gt;PyPy&lt;/a&gt;, &lt;a href=&quot;https://mypyc.readthedocs.io/en/latest/introduction.html&quot;&gt;Mypyc&lt;/a&gt;, &lt;a href=&quot;https://numba.pydata.org/&quot;&gt;Numba&lt;/a&gt;, &lt;a href=&quot;https://github.com/facebookincubator/cinder&quot;&gt;Cinder&lt;/a&gt;, or even Python-like
programming languages like &lt;a href=&quot;https://cython.org/&quot;&gt;Cython&lt;/a&gt;, &lt;a href=&quot;https://docs.exaloop.io/codon&quot;&gt;Codon&lt;/a&gt;, &lt;a href=&quot;https://www.modular.com/mojo&quot;&gt;Mojo&lt;/a&gt;&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;—I mean
the regular Python: &lt;a href=&quot;https://en.wikipedia.org/wiki/CPython&quot;&gt;CPython&lt;/a&gt;! The Python that is probably installed on your
computer right now. The Python that you got when you searched “python”
on Google and downloaded the first thing that came up. The Python that
you can pull up just by typing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python3&lt;/code&gt; into a fresh command line. That
Python. Python is a compiled language.&lt;/p&gt;

&lt;h1 id=&quot;some-context&quot;&gt;Some context&lt;/h1&gt;

&lt;p&gt;Currently, I’m working on material for teaching students how to read and
understand programming error messages. We are creating lessons for three
programming languages: C, Python, and Java. One of the key points in
teaching the nature of error messages is that they are &lt;em&gt;generated&lt;/em&gt;
at different stages: some error messages are reported at compile-time
and others are reported while the program is actively running.&lt;/p&gt;

&lt;p&gt;The first lesson was written for C, specifically using the GCC compiler.
What these lessons demonstrated was that GCC splits the task of
turning your code into a running program into various different stages:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;preprocessing&lt;/li&gt;
  &lt;li&gt;lexical analysis&lt;/li&gt;
  &lt;li&gt;syntactic analysis&lt;/li&gt;
  &lt;li&gt;semantic analysis&lt;/li&gt;
  &lt;li&gt;linking&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Furthermore, this lesson discusses the errors that may occur at each
stage, and how that will affect the error messages that are presented.
Importantly, &lt;strong&gt;an error in an earlier stage will prevent errors from
being detected at a later stage&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;While I was adapting this lesson for Java and Python, I realized that
some things would have to change—for example, neither Python or Java
have a preprocessor, and “linking” is not really the same thing in
either Python or Java, so I just skipped these topics. However,
I stumbled across an interesting insight.&lt;/p&gt;

&lt;h1 id=&quot;error-messages-can-help-you-discover-compilation-stages&quot;&gt;Error messages can help you discover compilation stages&lt;/h1&gt;

&lt;p&gt;The fact that error messages are generated by different stages of the
compiler, and compilers &lt;em&gt;generally&lt;/em&gt; issue errors from earlier stages
before continuing
also means that you can discover the stages of your compiler by
&lt;strong&gt;deliberately creating errors&lt;/strong&gt; in a program.&lt;/p&gt;

&lt;p&gt;So let’s discover the stages of the Python interpreter by playing a little
game I like to call…&lt;/p&gt;

&lt;h1 id=&quot;which-is-the-first-error&quot;&gt;Which! Is! The! First! Error!&lt;/h1&gt;

&lt;p&gt;We are going to create a Python program with &lt;strong&gt;several errors&lt;/strong&gt;, each of
which attempts to induce a different kind of error message. We know that
regular ol’ &lt;strong&gt;Python reports only one error message at a time&lt;/strong&gt;—so
the game is &lt;strong&gt;which error message will be reported first?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is the buggy program:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ñ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;hello
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Each line of code generates a different error message:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1 / 0 &lt;/code&gt; will generate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZeroDivisionError: division by zero&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print() = None&lt;/code&gt; will generate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxError: cannot assign to function call&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;if False&lt;/code&gt; will generate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxError: expected ':'&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ñ = &quot;hello&lt;/code&gt; will generate &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxError: EOL while scanning string literal&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The question is… which will be reported first? If you’re playing along
at home, note that, unless otherwise stated, I will be reporting error
messages from Python 3.12. Spoilers: the specific version of Python
matters (more than I thought it would) so keep that in mind if you see
different results.&lt;/p&gt;

&lt;h2 id=&quot;round-1&quot;&gt;Round 1&lt;/h2&gt;

&lt;p&gt;Now that we know the rules and the error messages that we are expecting,
let’s start! &lt;strong&gt;Without running the code&lt;/strong&gt;, which of the above error
messages will be reported first?&lt;/p&gt;

&lt;p&gt;Before I reveal the answer, think about what “interpreted” vs “compiled”
language means to you. Here is a terrible Socratic dialogue that will
hopefully make you personally reflect on what the difference is:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Socrates&lt;/strong&gt;: A compiled language is one where the code is first put through
a compiler before it is able to be run. An example is the C programming
language. To run C code, first you have to run a compiler like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;gcc&lt;/code&gt; or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;clang&lt;/code&gt;, and then finally you can run your code. A compiled language
gets converted to machine code—the ones and zeroes that your CPU
understands.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Plato&lt;/strong&gt;: But wait, isn’t Java a compiled language?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Socrates&lt;/strong&gt;: Yes, Java is a compiled language.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Plato&lt;/strong&gt;: But isn’t output of the regular Java compiler
a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.class&lt;/code&gt; file. That’s bytecode, isn’t it?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Socrates&lt;/strong&gt;: That’s correct. Bytecode isn’t machine code, but Java is still a compiled language.
This is because there are many problems that the compiler can catch,
so you will need to correct errors before your program starts running.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Plato&lt;/strong&gt;: What about interpreted languages?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Socrates&lt;/strong&gt;: An interpreted language is one that relies on a separate
program, aptly called an interpreter, to actually run your code. An
interpreted language does not require the programmer to run a compiler
first. Because of this, any errors that you make will be caught &lt;em&gt;while
your program is running&lt;/em&gt;. Python is an interpreted language—there is
no separate compiler, and &lt;em&gt;all errors that you make are caught at
runtime&lt;/em&gt;.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Plato&lt;/strong&gt;: If Python is not a compiled language, then why
does the standard library include modules called
&lt;a href=&quot;https://docs.python.org/3/library/py_compile.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;py_compile&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://docs.python.org/3/library/compileall.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compileall&lt;/code&gt;&lt;/a&gt;?&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Socrates&lt;/strong&gt;: Well, those modules just convert Python to bytecode.
They don’t convert Python to machine code, so Python is still an
interpreted language.&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Plato&lt;/strong&gt;: So, both Python and Java are converted to bytecode?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Socrates&lt;/strong&gt;: Correct.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;blockquote&gt;
  &lt;p&gt;&lt;strong&gt;Plato&lt;/strong&gt;: Then how is Python an interpreted language and yet
Java is a compiled language instead?&lt;/p&gt;

  &lt;p&gt;&lt;strong&gt;Socrates&lt;/strong&gt;: Because all errors in Python are caught at runtime.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay, enough of that. Let’s get to the actual answer. If you run code
above in Python 3.12…&lt;/p&gt;

&lt;p&gt;🥁🥁🥁🥁&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;You will get this error message:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  File &quot;/private/tmp/round_1.py&quot;, line 4
    ñ = &quot;hello  # SyntaxError: EOL while scanning string literal
        ^
SyntaxError: unterminated string literal (detected at line 4)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;The first error message detected is on the last line of source code&lt;/strong&gt;.
What this tells us is that &lt;strong&gt;Python must read the entire source code
file&lt;/strong&gt; before running the first line of code. If you have a definition
in your head of an “interpreted language” that includes “interpreted
languages run the code one line at a time”, then I want you to cross
that out!&lt;/p&gt;

&lt;h3 id=&quot;what-happened-here&quot;&gt;What happened here?&lt;/h3&gt;

&lt;p&gt;I haven’t done a deep dive into the source code of the CPython
interpreter to verify this, but I think the reason that this is the
first error detected is because one of the first steps that Python 3.12
does is &lt;em&gt;scanning&lt;/em&gt; (also known as &lt;em&gt;lexical analysis&lt;/em&gt;). The scanner
&lt;strong&gt;converts the ENTIRE file&lt;/strong&gt; into a series of &lt;em&gt;tokens&lt;/em&gt; before continuing
to the next stage. A missing quotation mark at the end of a string
literal is an error that is detected by the scanner—the scanner wants
to turn the ENTIRE string into one big token, but it can’t do that until
it finds the closing quotation mark. The scanner runs first, before
anything else in Python 3.12, hence why this is the first error message.&lt;/p&gt;

&lt;p&gt;Let’s close the quotation mark on line 4 to get rid of that error
message and try again.&lt;/p&gt;

&lt;h2 id=&quot;round-2&quot;&gt;Round 2&lt;/h2&gt;

&lt;p&gt;With our first error fixed, this is what our code looks like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ñ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;hello&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;There are still errors on lines 1, 2, and 3.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which! Is! The! First! Error!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Make an educated guess on which will be the first error reported.
I’ll spare you the strawman-y Socratic dialogue this time, and get
straight to the point.&lt;/p&gt;

&lt;p&gt;🥁🥁🥁🥁&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;File &quot;/private/tmp/round_2.py&quot;, line 2
    print() = None
    ^^^^^^^
SyntaxError: cannot assign to function call here. Maybe you meant '==' instead of '='?&quot;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxError&lt;/code&gt; on line 2!&lt;/p&gt;

&lt;h3 id=&quot;what-happened-here-1&quot;&gt;What happened here?&lt;/h3&gt;

&lt;p&gt;Once again, I did not check within the source code of CPython, but I am
reasonably certain that the next stage is &lt;strong&gt;parsing&lt;/strong&gt; (also known as
&lt;em&gt;syntactic analysis&lt;/em&gt;) and the parser reports the first error in the
source code. &lt;strong&gt;Parsing the whole file happens before running the first
line of code&lt;/strong&gt; which means that Python does not even see the error on
line 1 and reports the syntax error on line 2.&lt;/p&gt;

&lt;p&gt;At this point, I will point out now that the program I wrote
for this exercise is completely nonsensical and there is no right answer
for how to fix the error. I have no idea what might be intended by
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print() = None&lt;/code&gt;, so I will fix this by replacing it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;print(None)&lt;/code&gt;
which also doesn’t make sense, but at least it’s syntactically correct.&lt;/p&gt;

&lt;h2 id=&quot;round-3&quot;&gt;Round 3&lt;/h2&gt;

&lt;p&gt;We fixed one syntax error, but there’s still another two errors in our
file—one of which is also a syntax error. Here’s what our file looks
like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ñ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;hello&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Which! Is! The! First! Error!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recall that it seems that the syntax error took priority in round 2.
Will it be the same in round 3?&lt;/p&gt;

&lt;p&gt;🥁🥁🥁🥁&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  File &quot;/private/tmp/round_3.py&quot;, line 3
    if False
            ^
SyntaxError: expected ':'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yes! The syntax error on line 3 took priority over the error on line 1.&lt;/p&gt;

&lt;h3 id=&quot;what-happened-here-2&quot;&gt;What happened here&lt;/h3&gt;

&lt;p&gt;Just as in Round 2, the &lt;strong&gt;parser&lt;/strong&gt; sees the entire file first before
proceeding to the next stage. The fix is, as the error message
(implicitly) suggests, to insert a colon before the end of the line.&lt;/p&gt;

&lt;h3 id=&quot;aside-differences-between-python-versions&quot;&gt;Aside: Differences between Python versions&lt;/h3&gt;

&lt;p&gt;You might be wondering why I inserted two &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SyntaxError&lt;/code&gt;s in the same
file. Wouldn’t one be enough to show my point?&lt;/p&gt;

&lt;p&gt;And here’s where the &lt;em&gt;specific&lt;/em&gt; version of Python becomes relevant. If
you try the same exercise in Python 3.8 or earlier, the line numbers for
Rounds 2 and 3 are swapped!&lt;/p&gt;

&lt;p&gt;In Python 3.8, the first error message reported for Round 2 is on &lt;strong&gt;line
3&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;File &quot;round_2.py&quot;, line 3
    if False
            ^
SyntaxError: invalid syntax
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And after inserting the missing colon, Python 3.8 reports the following
error message on &lt;strong&gt;line 2&lt;/strong&gt;:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  File &quot;round_3.py&quot;, line 2
    print() = None
    ^
SyntaxError: cannot assign to function call
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Why are Python 3.8 and 3.12 reporting different errors? The reason is
that &lt;a href=&quot;https://peps.python.org/pep-0617/&quot;&gt;Python 3.9 introduced a new parser&lt;/a&gt;. This parser is more
capable than the previous, relatively naïve parser. The old parser
(an LL(1) for you parsing nerds) was incapable of looking more than one
token in advance which means that the parser in-and-of-itself
technically accepted Python programs that are syntactically-invalid.
Particularly, this limitation prevented the parser to identify whether
the left-hand side of an assignment is a valid assignment target.
&lt;a href=&quot;https://peps.python.org/pep-0617/#some-rules-are-not-actually-ll-1&quot;&gt;The PEP linked earlier&lt;/a&gt; notes that the old parser accepts
code that looks like this:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;y&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;But this code makes no sense! In fact, the full Python grammar
disallows this code. To fix this, a separate, hacky stage used to exist in
Python that would check all the assignments and make sure that the
left-side is actually something that can be assigned to. This occurred
&lt;em&gt;after&lt;/em&gt; parsing, hence why the error messages are swapped in the older
versions of Python.&lt;/p&gt;

&lt;h2 id=&quot;round-4&quot;&gt;Round 4&lt;/h2&gt;

&lt;p&gt;Okay, with the last syntax error fixed in our program, this is what our
program looks like:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;ñ&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;hello&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Okay, surely, &lt;em&gt;now&lt;/em&gt; the next error message to be reported is the error
on the first line. Right? …Right?&lt;/p&gt;

&lt;p&gt;🥁🥁🥁🥁&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;p&gt;.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Traceback (most recent call last):
  File &quot;/private/tmp/round_4.py&quot;, line 1, in &amp;lt;module&amp;gt;
    1 / 0
    ~~^~~
ZeroDivisionError: division by zero
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Yes, finally! At last, the error on the first line is reported.&lt;/p&gt;

&lt;p&gt;Note also that this error message makes the first appearance of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Traceback (most recent call last)&lt;/code&gt;—normally a staple of any Python
error message, here appearing for the first time in the final round.&lt;/p&gt;

&lt;h3 id=&quot;what-happened-here-3&quot;&gt;What happened here&lt;/h3&gt;

&lt;p&gt;Python was &lt;strong&gt;finally&lt;/strong&gt; able to start running the code. Once all of the syntax
errors are resolved, Python finally is able to interpret the
first line, where it is forced to divide a number by zero, raising
a runtime error called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZeroDivisionError&lt;/code&gt;! We know we’re in “runtime”
because Python has printed &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Traceback (most recent call last)&lt;/code&gt;
indicating that we have a stack trace. A stack trace is only something
that can exist during runtime, which means that this error
must have been caught during runtime.&lt;/p&gt;

&lt;p&gt;This implies that the errors encountered in rounds 1–3 were… not…
runtime errors? Then what were they?&lt;/p&gt;

&lt;h1 id=&quot;python-is-both-a-compiled-and-interpreted-language&quot;&gt;Python is both a compiled &lt;em&gt;and&lt;/em&gt; interpreted language&lt;/h1&gt;

&lt;p&gt;That’s right! The CPython interpreter really is an interpreter. But it
&lt;strong&gt;also&lt;/strong&gt; is a compiler. I hope the above exercise has demonstrated that
Python must go through a few stages before ever running the
first line of code:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;scanning&lt;/li&gt;
  &lt;li&gt;parsing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Older versions of Python added an additional stage:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;scanning&lt;/li&gt;
  &lt;li&gt;parsing&lt;/li&gt;
  &lt;li&gt;checking for valid assignment targets&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s compare this to the stages of compiling a C program from earlier:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;del&gt;preprocessing&lt;/del&gt;&lt;/li&gt;
  &lt;li&gt;lexical analysis (another term for “scanning”)&lt;/li&gt;
  &lt;li&gt;syntactic analysis (another term for “parsing”)&lt;/li&gt;
  &lt;li&gt;&lt;del&gt;semantic analysis&lt;/del&gt;&lt;/li&gt;
  &lt;li&gt;&lt;del&gt;linking&lt;/del&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Python still performs some compilation stages before running any code&lt;/strong&gt;.
Python really does compile your code first, and just like Java, it
compiles to bytecode. The implication for error reporting is that
&lt;strong&gt;Python has compiler error messages&lt;/strong&gt; and not every error message in
Python occurs during runtime—in fact, only one of the four error
messages above was raised at runtime, namely,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ZeroDivisionError: division by zero&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can actually compile all of your Python code beforehand using the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compileall&lt;/code&gt; module on the command line:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ python3 -m compileall .
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This will place the compiled bytecode of all Python files in the current
directory in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__pycache__/&lt;/code&gt; and show you any compiler errors.
If you want to know what actually is IN that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;__pycache__/&lt;/code&gt; folder,
&lt;a href=&quot;https://www.youtube.com/watch?v=5yqUTJuFuUk&amp;amp;t=7m11s&quot;&gt;I did a talk for EdmontonPy which you should check out&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;It is only &lt;em&gt;after&lt;/em&gt; Python has been compiled to bytecode that the
interpreter actually starts.&lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt; I hope that the previous exercise has
demonstrated that Python can indeed issue errors before runtime!&lt;/p&gt;

&lt;h1 id=&quot;compiled-vs-interpreted-language-is-a-false-dichotomy&quot;&gt;“Compiled vs. Interpreted language” is a false dichotomy&lt;/h1&gt;

&lt;p&gt;It’s a pet peeve of mine whenever a programming language is categorized
as either a “compiled” or “interpreted” language. A language is not
inherently compiled or interpreted; whether a language is compiled or
interpreted (or both!) is an &lt;strong&gt;implementation detail&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And I’m not the only one that thinks like this. Laurie Tratt has &lt;a href=&quot;https://tratt.net/laurie/blog/2023/compiled_and_interpreted_languages_two_ways_of_saying_tomato.html&quot;&gt;a
wonderful article&lt;/a&gt; arguing this exact same point by writing an
interpreter that gradually becomes an optimizing compiler. It’s really
neat!&lt;/p&gt;

&lt;p&gt;Another fantastic resource is Bob Nystrom’s Crafting Interpreters.
Here are some quotes from &lt;a href=&quot;https://craftinginterpreters.com/a-map-of-the-territory.html#compilers-and-interpreters&quot;&gt;chapter 2&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What’s the difference between a compiler and an interpreter?&lt;/p&gt;

  &lt;p&gt;It turns out this is like asking the difference between a fruit and
a vegetable. That seems like a binary either-or choice, but actually
“fruit” is a botanical term and “vegetable” is culinary. One does not
strictly imply the negation of the other. There are fruits that aren’t
vegetables (apples) and vegetables that aren’t fruits (carrots), but
also edible plants that are both fruits and vegetables, like tomatoes.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fun fact: this was when first I learned what the deal is with tomatoes.
In cooking, they’re a vegetable. And they’re, botanically, a fruit. They’re both.
They can be both, and that’s okay. Wait, weren’t we talking about
Python? Bob, bring us back:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;When you run your Python program using [CPython], the
code is parsed and converted to an internal bytecode format, which is
then executed inside the VM. From the user’s perspective, this is
clearly an interpreter—they run their program from source. But if you
look under CPython’s scaly skin, you’ll see that there is definitely
some compiling going on.&lt;/p&gt;

  &lt;p&gt;The answer is that it is both. CPython is an interpreter, and it has a compiler.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So why does it matter? Why is it counterproductive to make a hard
distinction between “compiled” and “interpreted” languages?&lt;/p&gt;

&lt;h1 id=&quot;compiled-vs-interpreted-limits-what-we-think-is-possible-with-programming-languages&quot;&gt;“Compiled vs. Interpreted” limits what we think is possible with programming languages&lt;/h1&gt;

&lt;p&gt;A programming language doesn’t have to be defined by whether it’s
compiled or interpreted! And thinking in such a rigid way limits what we
believe a given programming language can do.&lt;/p&gt;

&lt;p&gt;For instance, JavaScript is commonly lumped into the “interpreted
language” category. But for a while, JavaScript running in Google Chrome
would &lt;em&gt;never&lt;/em&gt; be interpreted—instead, JavaScript &lt;a href=&quot;https://wingolog.org/archives/2011/07/05/v8-a-tale-of-two-compilers&quot;&gt;was compiled
directly to machine code&lt;/a&gt;!&lt;sup id=&quot;fnref:4&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:4&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;4&lt;/a&gt;&lt;/sup&gt; As a result, &lt;a href=&quot;https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/node-gpp.html&quot;&gt;JavaScript
can keep pace with C++&lt;/a&gt;. For this reason, I am really
tired of arguments that say that interpreted languages are necessarily
slow—performance is multifaceted and depends on much more than the
“default” programming language implementation. JavaScript is fast now.
&lt;a href=&quot;https://github.com/ruby/ruby/blob/master/doc/yjit/yjit.md&quot;&gt;Ruby is fast now&lt;/a&gt;. Lua has been &lt;a href=&quot;https://luajit.org/luajit.html&quot;&gt;fast for a while&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What about programming languages that are typically labelled as compiled?&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/python-is-compiled/you-wouldnt-interpret-c.jpg&quot; alt=&quot;You wouldn't interpret a C program&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Do you want a C interpreter? &lt;a href=&quot;https://bellard.org/tcc/&quot;&gt;Here you go!&lt;/a&gt;
Now you can write shell scripts that feature undefined behaviour!&lt;/p&gt;

&lt;h2 id=&quot;repls-for-everybody&quot;&gt;REPLs for everybody!&lt;/h2&gt;

&lt;p&gt;One of the ways that I fell in love with programming was sitting in
front of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop&quot;&gt;REPL&lt;/a&gt; of languages like Python and Basic. That immediacy is
what made programming thrilling for me, and is why I often reach for
REPLs in my day-to-day life. But it seems like you can only have an interactive
prompt for interpreted languages—so you can’t have a REPL for compiled
languages, right?&lt;/p&gt;

&lt;p&gt;Consider the &lt;a href=&quot;https://www.haskell.org/ghc/&quot;&gt;Glasgow Haskell Compiler&lt;/a&gt; (GHC). One of the ways that
I learned Haskell (and promptly forgot it 😉) is by messing around in
the interactive console. But, as the name suggests, GHC really does
compile Haskell code.&lt;/p&gt;

&lt;p&gt;This makes being a “compiled” language no excuse for lacking an
interactive interpreter!
Do you want an interactive Java experience, similar to Python? It turns
out that &lt;a href=&quot;https://docs.oracle.com/javase/9/jshell/introduction-jshell.htm#JSHEL-GUID-630F27C8-1195-4989-9F6B-2C51D46F52C8&quot;&gt;jshell&lt;/a&gt; has been bundled with Java since 2017.&lt;/p&gt;

&lt;p&gt;But all of this is a distraction from the &lt;em&gt;real&lt;/em&gt; distinction that we
should be teaching students:&lt;/p&gt;

&lt;h1 id=&quot;the-true-distinction-static-vs-dynamic&quot;&gt;The true distinction: static vs. dynamic&lt;/h1&gt;

&lt;p&gt;The true distinction that we should be teaching students is the
difference between properties of languages that can be determined
&lt;strong&gt;statically&lt;/strong&gt;—that is, by just staring at the code without running
it—and properties that can only be known &lt;strong&gt;dynamically&lt;/strong&gt;, during
runtime.&lt;/p&gt;

&lt;p&gt;Notice that I said “properties” and not “languages”. Every programming
language chooses its own set of properties that can be determined either
statically or dynamically, and taken together, this makes a language
more “dynamic” or more “static”. Static versus dynamic is a &lt;em&gt;spectrum&lt;/em&gt;,
and yes, Python falls on the more dynamic end of the spectrum.
A language like Java has far more static features than Python, but even
Java includes things like &lt;a href=&quot;https://www.oracle.com/technical-resources/articles/java/javareflection.html&quot;&gt;reflection&lt;/a&gt;, which is
inarguably a dynamic feature.&lt;/p&gt;

&lt;p&gt;I find it understandable that dynamic vs. static is often conflated with
compiled vs. interpreted—usually, languages that use interpreters have
more dynamic features, like Python, Ruby, and JavaScript.
Languages with more static features tend to be implemented without
interpreters, like C++ and Rust. Then there’s Java that is somewhere in
the middle.&lt;/p&gt;

&lt;p&gt;Static type annotations in Python have been gradually (hehe) gaining
adoption in codebases, and one of the expectations is that this can
unlock performance benefits in Python code due to more things being
known statically. Unfortunately, the truth is not that simple. It turns
out that both types in Python (yes, just types in general—consider,
&lt;a href=&quot;https://docs.python.org/3/reference/datamodel.html#metaclasses&quot;&gt;metaclasses&lt;/a&gt;) and &lt;a href=&quot;https://www.youtube.com/watch?v=t863QfAOmlY&amp;amp;t=369s&quot;&gt;&lt;em&gt;annotations themselves&lt;/em&gt;&lt;/a&gt; are
dynamic features of Python, which makes static typing &lt;a href=&quot;https://bernsteinbear.com/blog/typed-python/&quot;&gt;not the boon for
performance that you would expect&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;It doesn’t matter whether Python is compiled or interpreted. What
matters is the set of properties that can be determined &lt;em&gt;statically&lt;/em&gt;
(that is, before runtime) in Python is relatively small, which means
more errors tend to make themselves apparent at runtime than in
programming languages that have more static features. This is the
actual distinction that matters, and it is a far more granular and
nuanced distinction than “compiled” vs “interpreted”. For this reason,
I think it is important to emphasize the particular static and dynamic
&lt;em&gt;features&lt;/em&gt; when rather than teaching the tired distinction between
“interpreted” and “compiled” languages.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: For source code for all the rounds (including a bonus round!)
check out my &lt;a href=&quot;https://github.com/eddieantonio/python-is-compiled&quot;&gt;GitHub repository&lt;/a&gt;.
You can also see the results for errors across different implementations
of Python, &lt;a href=&quot;https://github.com/eddieantonio/python-is-compiled/actions/runs/6642358433/job/18046941923&quot;&gt;check out the different jobs in GitHub Actions&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I did not realize just how many Python compilers/Python-like
languages exist. And this is very much a non-comprehensive list. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Nobody would ever say this. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Note that importing a module in Python happens at runtime, which
means that any syntax errors in an imported module will give you
error messages during runtime. You can still check for syntax errors
in imported modules beforehand by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;compileall&lt;/code&gt; module. &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:4&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;From what I understand, an interpreter called &lt;a href=&quot;https://v8.dev/blog/ignition-interpreter&quot;&gt;Ignition&lt;/a&gt; was
&lt;em&gt;added&lt;/em&gt; in 2016 to the V8 JavaScript engine, because the old
“full-codegen” ate up a giant amount of memory. &lt;a href=&quot;#fnref:4&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><summary type="html">This blog post hopes to convince you that Python is a compiled language. And by “Python”, I don’t mean alternate versions of Python like PyPy, Mypyc, Numba, Cinder, or even Python-like programming languages like Cython, Codon, Mojo1—I mean the regular Python: CPython! The Python that is probably installed on your computer right now. The Python that you got when you searched “python” on Google and downloaded the first thing that came up. The Python that you can pull up just by typing python3 into a fresh command line. That Python. Python is a compiled language. I did not realize just how many Python compilers/Python-like &amp;#8617;</summary></entry><entry><title type="html">{n} times faster than C …with Python</title><link href="https://eddieantonio.ca/blog/2023/07/12/faster-than-c-with-python/" rel="alternate" type="text/html" title="{n} times faster than C …with Python" /><published>2023-07-12T00:00:00+00:00</published><updated>2023-07-12T00:00:00+00:00</updated><id>https://eddieantonio.ca/blog/2023/07/12/faster-than-c-with-python</id><content type="html" xml:base="https://eddieantonio.ca/blog/2023/07/12/faster-than-c-with-python/">&lt;p&gt;I was reading Owen Shepherd’s post “&lt;a href=&quot;https://owen.cafe/posts/six-times-faster-than-c/&quot;&gt;{n} times faster than C&lt;/a&gt;”,
which explores how to hand-tune x86-64 assembly to make a certain
problem faster (see below). Originally, this inspired me to write a short
introduction to using Rust’s &lt;a href=&quot;https://github.com/rust-lang/portable-simd&quot;&gt;portable SIMD&lt;/a&gt; to manually speed up
problems like this. I rewrote the problem in Rust (of course), used
explicit &lt;a href=&quot;https://en.wikipedia.org/wiki/Single_instruction,_multiple_data&quot;&gt;SIMD&lt;/a&gt;, and observed a substantial speed-up.&lt;/p&gt;

&lt;p&gt;However, that is not why I’m writing this blog post. Before getting
started on writing that SIMD tutorial, I had an idea:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;What if I compare the performance of my hand-rolled SIMD solution to
a Python version that uses &lt;a href=&quot;https://numpy.org/&quot;&gt;NumPy&lt;/a&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This innocent thought, dear reader, destroyed my productivity for the
following two days. I learned, that you &lt;em&gt;can&lt;/em&gt; indeed write plain ol’
Python code that can compute certain problems faster than
a straightforward implementation in C and Rust.
The secret, of course, is to &lt;a href=&quot;https://www.youtube.com/watch?v=vVUnCXKuNOg&quot;&gt;write your Python program so that it’s
doing all its work outside of Python&lt;/a&gt;, but I’m getting ahead of
myself.&lt;/p&gt;

&lt;p&gt;So instead of a tutorial on portable SIMD, we get this blog post — I’ve
wasted so much time on this problem, I might as well write up some of my
findings to wrap things up. This is how Python can be more performant
than C or Rust.&lt;/p&gt;

&lt;p&gt;But first, what is the problem we’re trying to solve?&lt;/p&gt;

&lt;h1 id=&quot;the-problem&quot;&gt;The problem&lt;/h1&gt;

&lt;p&gt;The problem from &lt;a href=&quot;https://owen.cafe/posts/six-times-faster-than-c/&quot;&gt;Owen’s blog post&lt;/a&gt; is as follows:&lt;/p&gt;

&lt;p&gt;Given a null-terminated buffer, maintain a count: increment that count
for every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt; encountered. Decrement the count for every &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt;
encountered. Finally, return the count.&lt;/p&gt;

&lt;p&gt;Here is Owen’s original C solution:&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;run_switches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;switch&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'\0'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'s'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'p'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;res&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;nl&quot;&gt;default:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://owen.cafe/posts/six-times-faster-than-c/&quot;&gt;Owen’s post&lt;/a&gt; goes into detail about profiling this on his
x86-64 machine, including hand-tuning assembly. I’m running this on
my 2020 M1 MacBook Pro (see the appendix for more information on my
testing setup), which uses an entirely different architecture (ARM
AArch64 instead of x86-64), so Owen’s hand-tuned assembly won’t do me
any good. So how does this code run on my computer?&lt;/p&gt;

&lt;p&gt;I ran the above code with a test case of 12 MiB of random ASCII
printable characters and got the following results:&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;Runtime&lt;/dt&gt;
  &lt;dd&gt;7,794,642 nanoseconds per iteration 🦙&lt;/dd&gt;
  &lt;dt&gt;Throughput&lt;/dt&gt;
  &lt;dd&gt;1.478 GiB/s&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;With a test case of 12 MiB of random &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; bytes, I get the
following results:&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;Runtime&lt;/dt&gt;
  &lt;dd&gt;41,617,334 nanoseconds per iteration 🐢&lt;/dd&gt;
  &lt;dt&gt;Throughput&lt;/dt&gt;
  &lt;dd&gt;0.278 GiB/s or 284.9 MiB/s&lt;/dd&gt;
  &lt;dt&gt;Speedup&lt;/dt&gt;
  &lt;dd&gt;0.19x&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;I didn’t do any investigation into why the same code is so much slower
when given just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt; bytes, but if you peek at the compiled
machine code (see it here on &lt;a href=&quot;https://godbolt.org/z/foYrr1e6K&quot;&gt;Compiler Explorer&lt;/a&gt;),
it has 4 conditional branches. In general, modern CPUs work well if the
branches are few, and if the outcome of the branches that &lt;em&gt;do&lt;/em&gt; exist are
predictable. Preferably, the only conditional branch you have is the one
controlling the loop itself (e.g., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;while (condition) {...}&lt;/code&gt;). I’m
guessing that the latter test case seems to make the branch predictor
mispredict &lt;em&gt;a lot&lt;/em&gt;, which is why it’s about 20% of the speed.&lt;/p&gt;

&lt;p&gt;None of the remaining of the code examples seem to demonstrate an
appreciable difference on performance when given either input, so I will
only present the speed and any comparisons on the first test case,
namely the random ASCII printable test case.&lt;/p&gt;

&lt;h1 id=&quot;rewrite-it-in-rust&quot;&gt;Rewrite it in Rust™&lt;/h1&gt;

&lt;p&gt;Naturally, being a tediously predictable Rustacean, I had to rewrite
this in Rust. Here’s how that looks like:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ffi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;c&quot;&gt;// for C interoperability&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rust_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;c&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'s'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'p'&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;mi&quot;&gt;_&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;.sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;On the random ASCII test case, it had this performance:&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;Runtime&lt;/dt&gt;
  &lt;dd&gt;2,864,421 nanoseconds per iteration 🐆&lt;/dd&gt;
  &lt;dt&gt;Throughput&lt;/dt&gt;
  &lt;dd&gt;3.979 GiB/s&lt;/dd&gt;
  &lt;dt&gt;Speedup&lt;/dt&gt;
  &lt;dd&gt;2.72x&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;A few notes on this solution:&lt;/p&gt;

&lt;p&gt;Unlike the C version, the Rust version knows the length of the buffer,
and does not need to check for the null-terminator for each and every
byte of the input.&lt;/p&gt;

&lt;p&gt;Using iterators (that is, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s.to_bytes().iter()&lt;/code&gt;) tends to enable a lot
of optimizations in Rust, including &lt;a href=&quot;https://en.wikipedia.org/wiki/Automatic_vectorization&quot;&gt;auto-vectorization&lt;/a&gt; (that is,
automatically using SIMD; see below).&lt;/p&gt;

&lt;p&gt;Taking a quick peak at the assembly (see it here on &lt;a href=&quot;https://godbolt.org/z/jja8PMqTr&quot;&gt;Compiler
Explorer&lt;/a&gt;) shows the use of
instructions such as:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;        movi    v1.16b, #112
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Wow! &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;movi&lt;/code&gt; is creating 16 copies of the number #112 (the ASCII value of the letter
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt;) and putting them into a vector register &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;And look!&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;        ldr     q19, [x11], #16
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ldr&lt;/code&gt; loading 16 bytes from the input into a 128-bit wide-register &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;q19&lt;/code&gt;, while
incrementing the pointer by 16!&lt;/p&gt;

&lt;p&gt;And then you have this absolute banger:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-asm&quot;&gt;        cmeq    v20.16b, v19.16b, v1.16b
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Which is using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cmeq&lt;/code&gt; to simultaneously compare the 16 bytes from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v1&lt;/code&gt;
against the 16 bytes just loaded into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v19&lt;/code&gt; (a.k.a., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;q19&lt;/code&gt;, the data
from the input) and dumps the 16 individual comparisons into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v20&lt;/code&gt;. Nice!&lt;/p&gt;

&lt;p&gt;This is not the SIMD tutorial, so I won’t go into much depth about
what’s happening here, but the Rust compiler definitely figured out how
to automatically parallelize some parts of this algorithm, which is
cool, and partially explains the speedup. Loading 16 bytes at a time is
a whole lot faster than loading one byte at a time.&lt;/p&gt;

&lt;p&gt;That said, the vectorized code that the compiler generated was not
ideal. The majority of the instructions in the hot loop are the compiler
attempting to maintain 16 simultaneous 64-bit sums, but in order to do
that with the M1’s 128-bit wide SIMD registers, it must tediously shift bits
around. This uses a lot of instructions and a lot of registers, which is
work that should probably be avoided.&lt;/p&gt;

&lt;p&gt;I thought that I could do better.&lt;/p&gt;

&lt;h1 id=&quot;rust-portable-simd-the-fastest-solution&quot;&gt;Rust portable SIMD: The fastest solution&lt;/h1&gt;

&lt;p&gt;Here is the Rust version that uses portable SIMD:&lt;/p&gt;

&lt;div class=&quot;language-rust highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;ffi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nn&quot;&gt;simd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;u8x16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SimdInt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SimdPartialEq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;rust_portable_simd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CStr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isize&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_bytes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;middle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;bytes&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.as_simd&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;u8x16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;splat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'s'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;u8x16&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;splat&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'p'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;middle&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;neg_ss&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.simd_eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;neg_ps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.simd_eq&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.to_int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pairwise&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;neg_ps&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;neg_ss&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pairwise&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;.reduce_sum&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;isize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;mi&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count_scalar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;prefix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;count_scalar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;suffix&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;// Note: _count_scalar omitted for brevity&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can check out the assembly &lt;a href=&quot;https://godbolt.org/z/TPj7KTsaY&quot;&gt;in Compiler Explorer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So, how fast is this version?&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;Runtime&lt;/dt&gt;
  &lt;dd&gt;584,040 nanoseconds per iteration 🚀&lt;/dd&gt;
  &lt;dt&gt;Throughput&lt;/dt&gt;
  &lt;dd&gt;19.870 GiB/s&lt;/dd&gt;
  &lt;dt&gt;Speedup&lt;/dt&gt;
  &lt;dd&gt;13.3x (compared to original C version)&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;584,040 nanoseconds is a bit difficult for me to conceptualize. I think
milliseconds are bit easier to grok. 584,040 nanoseconds is 0.58
milliseconds. It took my three year old laptop just over half
a millisecond to analyze over 12 and a half million letters. That’s
sick. Computers are awesome. 🤘🏼&lt;/p&gt;

&lt;h1 id=&quot;predictably-slow-the-straightforward-python-solution&quot;&gt;Predictably slow: the straightforward Python solution&lt;/h1&gt;

&lt;p&gt;The way that I generated test data for my benchmarks was by using NumPy,
which is why I was using Python in the first place. Just for fun, let’s
see what happens when we use the most route one Python code that you can
imagine to iterate over the NumPy &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ndarray&lt;/code&gt; of random ASCII bytes and
get a solution:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;python_for_loop&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;elif&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;element&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;result&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And how fast was this on my machine?&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;Runtime&lt;/dt&gt;
  &lt;dd&gt;18,894,861,583 nanoseconds per iteration — that’s 18.89 seconds 🗿&lt;/dd&gt;
  &lt;dt&gt;Throughput&lt;/dt&gt;
  &lt;dd&gt;0.617 MiB/s (measuring in gigabytes per second is a bit optimistic 💀)&lt;/dd&gt;
  &lt;dt&gt;Speedup&lt;/dt&gt;
  &lt;dd&gt;0.000407x (compared to original C version)&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;So it turns out that calculating things one byte at a time in Python is
really, really slow. Who knew!&lt;/p&gt;

&lt;p&gt;However, there is another option.&lt;/p&gt;

&lt;h1 id=&quot;literally-the-first-thing-i-tried-in-numpy&quot;&gt;Literally the first thing I tried in NumPy&lt;/h1&gt;

&lt;p&gt;This is where we get into the day-ruiner. I can’t remember my thought
process clearly, but I have the vague notion in my head that operations
that operate on entire vectors are really fast in NumPy, so try to think
in vectors. And here is what I came up with:&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;python_numpy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;num_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count_nonzero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;s&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;num_p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count_nonzero&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;sa&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;p&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;num_p&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This counts how many &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt;s there are and then subtracts how many &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt;s
there are, and there’s your answer. How fast is it?&lt;/p&gt;

&lt;dl&gt;
  &lt;dt&gt;Runtime&lt;/dt&gt;
  &lt;dd&gt;1,992,393 nanoseconds per iteration 🏎&lt;/dd&gt;
  &lt;dt&gt;Throughput&lt;/dt&gt;
  &lt;dd&gt;5.800 GiB/s 🤯&lt;/dd&gt;
  &lt;dt&gt;Speedup&lt;/dt&gt;
  &lt;dd&gt;3.91x (compared to original C version)&lt;/dd&gt;
&lt;/dl&gt;

&lt;p&gt;Yes. &lt;strong&gt;NumPy is nearly four times as fast as C&lt;/strong&gt;.
Of course, the original C code is hampered by having to iterate
byte-by-byte to look for that null-terminator, not to mention that NumPy
itself is a library written in C and Fortran, and it uses explicit SIMD parallelism
to do its operations quickly.&lt;/p&gt;

&lt;p&gt;However, I spent &lt;em&gt;hours&lt;/em&gt; trying to replicate this algorithm with all its
irreducible steps in Rust. That is, I could not find a way to write
Rust code that could do the following:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Allocate a boolean vector whose elements are true if the
corresponding byte in the input is equal to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Allocate a boolean vector whose elements are true if the
corresponding byte in the input is equal to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;p&lt;/code&gt;.&lt;/li&gt;
  &lt;li&gt;Count how many non-zero values appear in the first vector.&lt;/li&gt;
  &lt;li&gt;Count how many non-zero values appears in the second vector.&lt;/li&gt;
  &lt;li&gt;Subtract the counts.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ll spare you the details, but in my attempts to write Rust code that
faithfully does all these steps – steps that NumPy &lt;em&gt;must&lt;/em&gt; do, given
it’s working in Python – the best I could do is a solution that
takes 3,769,195 nanoseconds per iteration or 3.031 GiB/s.
It took me an entire working day of doing this, employing
nasty tricks like using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::mem::transmute::&amp;lt;&amp;amp;[bool], &amp;amp;[u8]&amp;gt;(input)&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unsafe { vec.set_len(input.len()) }&lt;/code&gt;. It got ugly! And I never managed
to write any Rust code that did all that much faster than NumPy. 😖&lt;/p&gt;

&lt;h1 id=&quot;final-results&quot;&gt;Final results&lt;/h1&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;Implementation&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Time per iteration&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Throughput&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Speedup&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;🥇&lt;/td&gt;
      &lt;td&gt;Rust - portable SIMD&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;584,040 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;19.870 GiB/s&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;13.3x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;🥈&lt;/td&gt;
      &lt;td&gt;Python - NumPy&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1,992,393 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;5.800 GiB/s&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;3.91x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;🥉&lt;/td&gt;
      &lt;td&gt;Rust - iterators&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;2,864,421 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;3.979 GiB/s&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;2.72x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;C - original&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;7,794,642 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1.478 GiB/s&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;1.00x&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;Python - byte-by-byte&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;18,894,861,583 ns&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&amp;lt;0.001 GiB/s&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;&amp;lt;0.01x&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;So Rust, using a manually-crafted SIMD algorithm is still the winner,
but I am frankly alarmed to know that code can be nearly 4 times as fast
as C with just NumPy, having to write only 4 lines of Python code.&lt;/p&gt;

&lt;p&gt;There’s a lot more I could do here to investigate performance
differences—I did not really employ any fancy profilers. I started to
investigate &lt;a href=&quot;https://github.com/numpy/numpy/blob/c6a449c7972e97afd9401d098939fe29d3e7c891/numpy/core/src/multiarray/item_selection.c#L2377-L2395&quot;&gt;NumPy’s source code&lt;/a&gt; and confirmed that the
developers indeed wrote explicit SIMD, but that’s as far as I got. I’m
curious how fast some straightforward &lt;a href=&quot;https://www.pola.rs/&quot;&gt;Polars&lt;/a&gt; implementation would
be.&lt;/p&gt;

&lt;h1 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h1&gt;

&lt;p&gt;You might not need to rewrite it in Rust, if you can somehow get NumPy
to do all the heavy-lifting for you.&lt;/p&gt;

&lt;h1 id=&quot;appendix-benchmarking-set-up&quot;&gt;Appendix: Benchmarking set up&lt;/h1&gt;

&lt;p&gt;You can find all my code, including my janky benchmarking setup in
&lt;a href=&quot;https://github.com/eddieantonio/fast-sp/&quot;&gt;this GitHub repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For all results, I used the same 12 MiB test case. For Rust and C,
I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cargo bench&lt;/code&gt; to run benchmarks, however &lt;a href=&quot;https://doc.rust-lang.org/nightly/unstable-book/library-features/test.html&quot;&gt;the docs are
sparse&lt;/a&gt;, so I’m not entirely sure how many iterations were
taken. For Python, I used &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;timeit&lt;/code&gt;, taking 5 samples with 200 iterations
per sample and reported the fastest mean time.&lt;/p&gt;

&lt;p&gt;I compiled all C code with Clang with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-O2&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-mcpu=apple-m1&lt;/code&gt; to try
to coax LLVM to generate code best suited for my machine. I compiled
all Rust code with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--release&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here are the exact versions of the software I used.&lt;/p&gt;

&lt;p&gt;Clang:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ clang --version
Apple clang version 14.0.0 (clang-1400.0.29.202)
Target: arm64-apple-darwin21.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Rust:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ rustc --version
rustc 1.73.0-nightly (8ca44ef9c 2023-07-10)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Python and NumPy:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ python3 --version
Python 3.11.0
$ python3 -c &quot;import numpy; print('NumPy', numpy.__version__)&quot;
NumPy 1.25.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The machine I was testing on is just my personal laptop, a 2020 M1
MacBook Pro. Details of its specs are a bit complicated (4 performance
cores?! 4 energy cores?! What? It has &lt;em&gt;how&lt;/em&gt; large of a shared L2
cache?), but here is what macOS tells me:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ uname -srm
Darwin 21.6.0 arm64
$ sysctl machdep.cpu.brand_string machdep.cpu.core_count hw.l1icachesize hw.l1dcachesize hw.l2cachesize
machdep.cpu.brand_string: Apple M1
machdep.cpu.core_count: 8
hw.l1icachesize: 131072
hw.l1dcachesize: 65536
hw.l2cachesize: 4194304
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><summary type="html">I was reading Owen Shepherd’s post “{n} times faster than C”, which explores how to hand-tune x86-64 assembly to make a certain problem faster (see below). Originally, this inspired me to write a short introduction to using Rust’s portable SIMD to manually speed up problems like this. I rewrote the problem in Rust (of course), used explicit SIMD, and observed a substantial speed-up.</summary></entry><entry><title type="html">Installing Python on macOS (without going insane)</title><link href="https://eddieantonio.ca/blog/2020/01/26/installing-python-on-macos/" rel="alternate" type="text/html" title="Installing Python on macOS (without going insane)" /><published>2020-01-26T00:00:00+00:00</published><updated>2020-01-26T00:00:00+00:00</updated><id>https://eddieantonio.ca/blog/2020/01/26/installing-python-on-macos</id><content type="html" xml:base="https://eddieantonio.ca/blog/2020/01/26/installing-python-on-macos/">&lt;p&gt;If you’re like me (or &lt;a href=&quot;https://xkcd.com/&quot;&gt;Randall Munroe&lt;/a&gt;), the results of typing
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;which -a python&lt;/code&gt; on your macOS machine is… devastating:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://xkcd.com/1987/&quot;&gt;&lt;img src=&quot;https://imgs.xkcd.com/comics/python_environment.png&quot; alt=&quot;xkcd about Python path&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After many years of frustration, here are my recommendations for
installing Python and Python-written utilities on macOS.&lt;/p&gt;

&lt;h2 id=&quot;so-what-do-i-use-python-for&quot;&gt;So what do I use Python for?&lt;/h2&gt;

&lt;p&gt;I use Python for &lt;strong&gt;library development&lt;/strong&gt;, &lt;strong&gt;web development with
Django&lt;/strong&gt;, and &lt;strong&gt;scripting&lt;/strong&gt;. I use Python primarily from the &lt;strong&gt;command
line&lt;/strong&gt;. I don’t really use Python for data science—at least not with
Jupyter notebooks. I have absolutely no idea how to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;conda&lt;/code&gt;. If you
use Python primarily for data science, this guide is &lt;em&gt;not&lt;/em&gt; written
for you—there may be better solutions that I simply do not use.
If your use case sounds similar to mine, please read on!&lt;/p&gt;

&lt;h1 id=&quot;do-not-install-python-from-pythonorg&quot;&gt;Do &lt;strong&gt;NOT&lt;/strong&gt; install Python from python.org&lt;/h1&gt;

&lt;p&gt;It seems pretty obvious that you should install Python using the
installer from &lt;a href=&quot;https://www.python.org/&quot;&gt;Python’s official website&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/installing-python/do-you-want-to-die.jpg&quot; alt=&quot;Do you want to die?&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The problem with this is that Python installs itself in a place that is
difficult to manage &lt;em&gt;without&lt;/em&gt; using administrator (i.e., &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt;) privileges.
This is okay for beginners, or people who only touch Python every so
often. However, for &lt;em&gt;my&lt;/em&gt; use cases, where I’m testing my code in
multiple versions of Python, and have multiple &lt;a href=&quot;https://docs.python.org/3/tutorial/venv.html&quot;&gt;virtual environments&lt;/a&gt;,
this becomes bad news.&lt;/p&gt;

&lt;h1 id=&quot;homebrew&quot;&gt;Homebrew&lt;/h1&gt;

&lt;p&gt;If you haven’t used &lt;a href=&quot;https://brew.sh/&quot;&gt;Homebrew&lt;/a&gt; to install things on your Mac, go get it
right now! I’ll wait.&lt;/p&gt;

&lt;p&gt;We’re going to use Homebrew to install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Do &lt;strong&gt;not&lt;/strong&gt; install Python with Homebrew, though. Instead…&lt;/p&gt;

&lt;h1 id=&quot;manage-multiple-python-versions-with-pyenv&quot;&gt;Manage multiple Python versions with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt;&lt;/h1&gt;

&lt;p&gt;Install &lt;a href=&quot;https://github.com/pyenv/pyenv&quot;&gt;pyenv&lt;/a&gt; so that you can install multiple versions of Python
and switch between them with ease:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;pyenv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;putting-pyenv-in-charge-by-adding-it-to-the-path&quot;&gt;Putting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt; in charge by adding it to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PATH&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;Now you need to make sure your system can see the &lt;strong&gt;shims&lt;/strong&gt; that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt;
creates. Shims are fake versions of &lt;em&gt;all&lt;/em&gt; programs associated with
a Python install. This allows &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt; to intercept a command and
redirect it to the current or desired version of Python.&lt;/p&gt;

&lt;p&gt;To do this, &lt;strong&gt;prepend&lt;/strong&gt; the shim directory to your path. Add this line to the
&lt;strong&gt;bottom&lt;/strong&gt; of your &lt;a href=&quot;https://stackoverflow.com/questions/15101559/terminal-where-is-the-shell-start-up-file&quot;&gt;shell startup file&lt;/a&gt; (either &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.zshrc&lt;/code&gt; or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;~/.bash_profile&lt;/code&gt; depending on either how new your Mac is or if you’ve
customized your shell ¯\_(ツ)_/¯):&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PATH&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/.pyenv/shims:&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$PATH&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The shim directory must be the &lt;strong&gt;first&lt;/strong&gt; thing in your path, or at
least, it has to come &lt;em&gt;before&lt;/em&gt; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/bin/&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local/bin&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that you have added the line to your shell startup file,
&lt;strong&gt;restart your shell&lt;/strong&gt; for it to take effect. You can do this by closing
your current terminal tab/window, and opening a new
one, or in the same terminal/tab, you can do the following:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SHELL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Let’s make sure our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PATH&lt;/code&gt; looks right. To look at your path, use the
following line:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;printenv &lt;/span&gt;PATH
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is what my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PATH&lt;/code&gt; environment variable looks like on my laptop:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/Users/eddieantonio/.pyenv/shims:/usr/local/bin:/usr/local/sbin:/bin:/usr/sbin:/sbin:/Library/TeX/texbin:/opt/X11/bin:/Users/eddieantonio/.local/bin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;small&gt;Kind of. I removed a few entries from my PATH to save myself from
embarrassment.&lt;/small&gt;&lt;/p&gt;

&lt;h2 id=&quot;installing-a-python-version&quot;&gt;Installing a Python version!&lt;/h2&gt;

&lt;p&gt;Now let’s install a modern version of Python. As of this writing, the
current stable version of &lt;a href=&quot;https://en.wikipedia.org/wiki/CPython&quot;&gt;CPython&lt;/a&gt; is Python 3.8.1. Let’s install it with
Pyenv!&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pyenv &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;3.8.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you want to see all the versions of Python that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt; can install
for you, you can list them:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pyenv &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--list&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This should be a pretty long list, with all kinds of alternate Python
interpreters, including &lt;a href=&quot;https://pypy.org/&quot;&gt;PyPy&lt;/a&gt; and &lt;a href=&quot;https://github.com/stackless-dev/stackless&quot;&gt;Stackless&lt;/a&gt;. Install as many as
you need!&lt;/p&gt;

&lt;h2 id=&quot;choose-your-default-python&quot;&gt;Choose your default Python&lt;/h2&gt;

&lt;p&gt;Now tell &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt; the default version you want to use. Do this with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv
global&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pyenv global 3.8.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now try it! Check what happens when you ask for the current Python
version:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;$ python --version
Python 3.8.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Since I’m paranoid that &lt;em&gt;something&lt;/em&gt; on my system will break because it’s
expecting &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;python&lt;/code&gt; to be Python 2.7 (yikes!) that’s pre-installed by
Apple on my system, I do the following:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pyenv global system 3.8.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This instructs to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt; that it should check if the executable is
installed on the system path first, and &lt;em&gt;then&lt;/em&gt; try the selected Python
version—in my case, CPython 3.8.1. You might be able to get away with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv global 3.8.1&lt;/code&gt; and not use the pre-installed system Python at all.&lt;/p&gt;

&lt;p&gt;If you don’t see the version you want to install, perhaps your installed version of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt; is too old. Upgrade &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pyenv&lt;/code&gt; using Homebrew:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew upgrade pyenv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;pipx&quot;&gt;pipx&lt;/h1&gt;

&lt;p&gt;I often want to install utilities that are written in Python, and
available to install via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;. For example, I like installing
&lt;a href=&quot;https://black.readthedocs.io/en/stable/&quot;&gt;black&lt;/a&gt; to format my code for me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do not&lt;/strong&gt; do the following 🙅:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;black
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Instead, I’d like these globally-installed executables to be installed,
without installing themselves on my Python path.&lt;/p&gt;

&lt;p&gt;This is exactly the problem that &lt;a href=&quot;https://github.com/pipxproject/pipx/&quot;&gt;pipx&lt;/a&gt; tries to solve.&lt;/p&gt;

&lt;p&gt;Install it using Homebrew:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;pipx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then install your favourite Python executables 👍!&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pipx &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;black
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sadly, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pipx&lt;/code&gt; will only install one package at a time, but we can do
some arcane shell-fu to install multiple packages in one command line.
Use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xargs -n1&lt;/code&gt; to call an command with exactly one of its input
arguments:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install isort, mypy, snakeviz, pygments, and tqdm all on one line!&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;isort mypy snakeviz pygments tqdm | xargs &lt;span class=&quot;nt&quot;&gt;-n1&lt;/span&gt; pipx &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;managing-project-environments-with-poetry&quot;&gt;Managing project environments with poetry&lt;/h1&gt;

&lt;p&gt;For each of my projects, I want an isolated environment to install
packages. Python has historically done this… poorly. The hacky
solution for years has been &lt;a href=&quot;https://docs.python.org/3/tutorial/venv.html&quot;&gt;virtual environments&lt;/a&gt;, but they require
a lot of manual work, and you need to remember to create a virtual environment,
activate it, place all the packages you installed in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requirements.txt&lt;/code&gt;,
and so on.
What a mess!&lt;/p&gt;

&lt;p&gt;Then came &lt;a href=&quot;https://github.com/pypa/pipenv&quot;&gt;pipenv&lt;/a&gt;. I’ve used pipenv for about a year, and noticed
a notable improvement in my workflow. However, some of the decisions
that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pipenv&lt;/code&gt;’s original creator made were questionable to say the
least. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pipenv&lt;/code&gt; is under new management, but some of the decisions and
attitudes of the original creator have left scars on the current
state of the project.&lt;/p&gt;

&lt;p&gt;So then I switched to &lt;a href=&quot;https://github.com/python-poetry/poetry&quot;&gt;Poetry&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Poetry is my current choice for project dependency management. You can
install it with Homebrew:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;poetry
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Basic usage can be found on &lt;a href=&quot;https://python-poetry.org/docs/basic-usage/&quot;&gt;Poetry’s website&lt;/a&gt;, but here’s
a quick tutorial:&lt;/p&gt;

&lt;p&gt;Make a brand new project using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poetry new&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;poetry new my-awesome-app
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt; into the project, and install some dependencies:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;my-awesome-app
poetry add django
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can also specify dependencies you only use in development—that is,
these packages should &lt;em&gt;not&lt;/em&gt; be installed when somebody downloads your
library, or deploys your app somewhere.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;poetry add &lt;span class=&quot;nt&quot;&gt;--dev&lt;/span&gt; black isort pylint
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;To actually &lt;em&gt;run&lt;/em&gt; my application, I tend to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;poetry shell&lt;/code&gt; to
activate the current environment.&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;poetry shell
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For example, in my Django app:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# this will not work -- not in virtualenv:
$ django-admin version
zsh: command not found: django-admin
$ poetry shell
Spawning shell within /Users/eddieantonio/Library/Caches/pypoetry/virtualenvs/my-awesome-app-VMVa3PrX-py3.7
# this works now 😄
(my-awesome-app-VMVa3PrX-py3.7) $ django-admin version
3.0.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This is just scratching the surface of using Poetry. &lt;a href=&quot;https://python-poetry.org/docs/basic-usage/&quot;&gt;Read the
docs&lt;/a&gt; to learn more.&lt;/p&gt;

&lt;h1 id=&quot;putting-it-all-together-installing-python-on-macos&quot;&gt;Putting it all together: installing Python on macOS&lt;/h1&gt;

&lt;p&gt;Here is a dangerously copy-pastable snippet to install all the things:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# Install Homebrew:&lt;/span&gt;
/usr/bin/ruby &lt;span class=&quot;nt&quot;&gt;-e&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;curl &lt;span class=&quot;nt&quot;&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/Homebrew/install/master/install&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Install pyenv, pipx, and poetry with Homebrew:&lt;/span&gt;
brew &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;pyenv pipx poetry

&lt;span class=&quot;c&quot;&gt;# Make sure pyenv is on the path:&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;'export PATH=&quot;$HOME/.pyenv/shims:$PATH&quot;'&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; ~/.zshrc

&lt;span class=&quot;c&quot;&gt;# Restart your shell (so we have the updated path):&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$SHELL&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Install a modern version of Python using pyenv:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# (Python 3.8.1 is the latest stable version as of this writing)&lt;/span&gt;
pyenv &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;3.8.1

&lt;span class=&quot;c&quot;&gt;# Tell pyenv to try using the system Python 2.x before using our pyenv&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# installed Python 3.8.1&lt;/span&gt;
pyenv global system 3.8.1

&lt;span class=&quot;c&quot;&gt;# Install Python-based utilities globally using pipx:&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# May I suggest black, isort, and mypy?&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;echo &lt;/span&gt;black isort mypy | xargs &lt;span class=&quot;nt&quot;&gt;-n1&lt;/span&gt; pipx &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Happy coding! 🍎🐍&lt;/p&gt;</content><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><summary type="html">If you’re like me (or Randall Munroe), the results of typing which -a python on your macOS machine is… devastating:</summary></entry><entry><title type="html">Deploying a Parcel app with Now</title><link href="https://eddieantonio.ca/blog/2018/12/09/deploying-a-parcel-app-with-now/" rel="alternate" type="text/html" title="Deploying a Parcel app with Now" /><published>2018-12-09T00:00:00+00:00</published><updated>2018-12-09T00:00:00+00:00</updated><id>https://eddieantonio.ca/blog/2018/12/09/deploying-a-parcel-app-with-now</id><content type="html" xml:base="https://eddieantonio.ca/blog/2018/12/09/deploying-a-parcel-app-with-now/">&lt;p&gt;Last week, I deployed &lt;a href=&quot;https://syllabics.app/&quot;&gt;a single-page web application&lt;/a&gt; on &lt;a href=&quot;https://zeit.co/now&quot;&gt;Now.sh&lt;/a&gt; built with the &lt;a href=&quot;https://parceljs.org/&quot;&gt;Parcel&lt;/a&gt; bundler. Here’s how!&lt;/p&gt;

&lt;h2 id=&quot;setup&quot;&gt;Setup&lt;/h2&gt;

&lt;p&gt;The files of my web application started off looking like this:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;.
├── img/
├── index.html
├── main.js
├── package.json
└── styles.css
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A single &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; entry point, with logic in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main.js&lt;/code&gt; and styles provided by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;styles.css&lt;/code&gt;. There are some PNGs in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;img/&lt;/code&gt; used for favicons. They’re all authored in a human-readable way—that is, with whitespace, and meaningful variable names.&lt;/p&gt;

&lt;p&gt;Let’s bundle this with &lt;a href=&quot;https://parceljs.org/&quot;&gt;Parcel&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;First, install &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;parcel-bundler&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;parcel-bundler &lt;span class=&quot;nt&quot;&gt;--save-dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then, build &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; with Parcel:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npx parcel build index.html
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Next, in a new folder called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dist/&lt;/code&gt; you’ll see &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; and all the files included by it in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dist/&lt;/code&gt;. But, they’ll be &lt;a href=&quot;https://en.wikipedia.org/wiki/Minification_(programming)&quot;&gt;minified&lt;/a&gt; and &lt;a href=&quot;https://en.parceljs.org/production.html#file-naming-strategy&quot;&gt;content hashed&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;With minification, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;index.html&lt;/code&gt; gets a modest 9.2% size decrease from 3.8 KiB to 3.45 KiB. However, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;styles.css&lt;/code&gt; goes from 2.7 KiB to  995 bytes, a 63.1% decrease!&lt;/p&gt;

&lt;h2 id=&quot;setting-up-packagejson&quot;&gt;Setting up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;I only want to commit my &lt;em&gt;source code&lt;/em&gt; to GitHub; I don’t want to remember to build my package and push the resulting build products every time I change a single line of code (I know &lt;a href=&quot;https://git-scm.com/book/uz/v2/Customizing-Git-Git-Hooks&quot;&gt;git pre-commit hooks&lt;/a&gt; exist, but… eww, no). Luckily, Now will run a build step for you before deploy!&lt;/p&gt;

&lt;p&gt;I used Now’s official &lt;a href=&quot;https://zeit.co/docs/v2/deployments/official-builders/static-build-now-static-build#how-to-use-it&quot;&gt;static builder&lt;/a&gt;. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@now/static-builder&lt;/code&gt; will run a script specified in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt;. It expects that the results of the build will be created in a folder called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dist/&lt;/code&gt;. It just so happens that &lt;strong&gt;Parcel places build output in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;dist/&lt;/code&gt; by default&lt;/strong&gt;!&lt;/p&gt;

&lt;p&gt;To setup, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@now/static-builder&lt;/code&gt;, I added the following line to the &lt;a href=&quot;https://docs.npmjs.com/files/package.json#scripts&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;scripts&lt;/code&gt; key&lt;/a&gt; in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;package.json&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;now-build&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;parcel build index.html&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
   &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Notice how this is the same line as before, just minus the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npx&lt;/code&gt; part.&lt;/p&gt;

&lt;p&gt;The nice thing about this, is that we can test if our build works by running the following command locally:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm run now-build
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;setting-up-nowjson&quot;&gt;Setting up &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;now.json&lt;/code&gt;&lt;/h2&gt;

&lt;p&gt;To configure the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@now/static-build&lt;/code&gt; builder, add a line to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;builds&lt;/code&gt; key in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;now.json&lt;/code&gt;. Luckily, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;now.json&lt;/code&gt; doesn’t tend to have a lot of configuration, so the following is a complete &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;now.json&lt;/code&gt; that you can copy-paste, and it will work in your app:&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;builds&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;src&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;package.json&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;use&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;@now/static-build&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;deploying&quot;&gt;Deploying&lt;/h2&gt;

&lt;p&gt;From here, it’s up to your Now setup. I tend to push to a new branch in GitHub, and create a pull request. With &lt;a href=&quot;https://zeit.co/github&quot;&gt;Now’s GitHub Integration&lt;/a&gt;, this will automatically start the build and deploy it when it’s successful. I’ve added an alias to my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;now.json&lt;/code&gt;, so the latest pull request will be deployed as soon as it’s merged.&lt;/p&gt;

&lt;h2 id=&quot;other-things&quot;&gt;Other things&lt;/h2&gt;

&lt;p&gt;There’s an custom &lt;a href=&quot;https://github.com/sergiodxa/now-parcel&quot;&gt;Now builder on npm&lt;/a&gt;, but I decided against using this, I’m wary of adding low star-count dependencies, ever since that &lt;a href=&quot;https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident&quot;&gt;event-stream incident&lt;/a&gt;. It turns out that using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;@now/static-build&lt;/code&gt; was fairly straightforward, so the custom package didn’t seem worth adding an extra requirement.&lt;/p&gt;

&lt;p&gt;That’s all I got. I really like how relatively simple and painless it was to create a fast, minified, cache-friendly single-page web application with Now and Parcel. Getting a neat domain was pretty simple too! It’s bring us back to the simpler times when we’d deploy a new version of our application by uploading our PHP file via FTP to some random server. Ah, memories!&lt;/p&gt;</content><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><summary type="html">Last week, I deployed a single-page web application on Now.sh built with the Parcel bundler. Here’s how!</summary></entry><entry><title type="html">Why I made yet another Cree syllabics converter</title><link href="https://eddieantonio.ca/blog/2018/07/30/why-i-made-yet-another-cree-syllabics-converter/" rel="alternate" type="text/html" title="Why I made yet another Cree syllabics converter" /><published>2018-07-30T00:00:00+00:00</published><updated>2018-12-06T00:00:00+00:00</updated><id>https://eddieantonio.ca/blog/2018/07/30/why-i-made-yet-another-cree-syllabics-converter</id><content type="html" xml:base="https://eddieantonio.ca/blog/2018/07/30/why-i-made-yet-another-cree-syllabics-converter/">&lt;p&gt;&lt;img src=&quot;/blog/images/yasc/ruben-quinn-star-chart.jpg&quot; alt=&quot;Ruben Quinn demonstrating the Cree syllabics star chart&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt; Image source: &lt;a href=&quot;https://www.youtube.com/watch?v=_08Kxo424sg&quot;&gt;Ruben Quinn demonstrating the Cree syllabics star chart&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Lately, I’ve been working on language technology for &lt;a href=&quot;https://en.wikipedia.org/wiki/Plains_Cree&quot;&gt;Plains Cree&lt;/a&gt;.
Plains Cree is primarily written in two systems: &lt;strong&gt;standard Roman
orthography&lt;/strong&gt; (&lt;strong&gt;SRO&lt;/strong&gt;) and &lt;strong&gt;syllabics&lt;/strong&gt;. Since SRO uses the Latin
alphabet—just like English—it is rather straightforward to type on
a standard Canadian English keyboard. Syllabics keyboards are an ongoing
struggle, however (I may expand on this some other time on this blog).
Therefore, if one wants to write Cree in syllabics, it is sometimes
easier to type it in SRO first, then use a &lt;em&gt;transliterator&lt;/em&gt; to convert
from SRO to syllabics.&lt;/p&gt;

&lt;p&gt;In summary, I created a new bidirectional transliterator called
&lt;a href=&quot;https://github.com/eddieantonio/cree-sro-syllabics&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cree-sro-syllabics&lt;/code&gt;&lt;/a&gt;
(&lt;strong&gt;Edit&lt;/strong&gt;: Try it online at &lt;a href=&quot;https://syllabics.app/&quot;&gt;syllabics.app&lt;/a&gt;!).
It’s a &lt;em&gt;new&lt;/em&gt; transliterator, because several
transliterators already exist. So what do the others look like?&lt;/p&gt;

&lt;h2 id=&quot;what-are-the-freely-available-transliterators&quot;&gt;What are the freely available transliterators?&lt;/h2&gt;

&lt;p&gt;A quick Google search will net you at least the following SRO to
syllabics transliterators.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.creedictionary.com/converter/maskwacis.php&quot;&gt;The Maskwacîs Plains Cree Syllabic Converter&lt;/a&gt;&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://syllabics.atlas-ling.ca/&quot;&gt;The Algonquian Linguistic Atlas Cree Syllabics Converter&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.syllabics.net/convert/plainscree&quot;&gt;Syllabics.net’s Plains Cree Syllabics Converter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, none of these transliterators are perfect.&lt;/p&gt;

&lt;h2 id=&quot;the-issues&quot;&gt;The issues&lt;/h2&gt;

&lt;h3 id=&quot;word-final-hk&quot;&gt;Word final “hk”&lt;/h3&gt;

&lt;p&gt;In syllabics, a word that ends with an “hk”—or “ᐦᐠ” in syllabics—are
supposed end with “ᕽ” instead. However, this replacement can never occur
in the middle of a word.&lt;/p&gt;

&lt;p&gt;For example, the word “ê-wêpâpîhkêwêpinamâhk” (“we (and not you) are
setting it swinging”), contains both a final “hk” and a “hk” cluster in
the middle of the word. Its syllabic transcription is &lt;strong&gt;ᐁᐍᐹᐲᐦᑫᐍᐱᐊᒫᕽ&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Although &lt;a href=&quot;https://syllabics.atlas-ling.ca/&quot;&gt;the Algonquian Linguistic Atlas’s converter&lt;/a&gt; and
&lt;a href=&quot;http://www.syllabics.net/convert/plainscree&quot;&gt;syllabics.net’s converter&lt;/a&gt; both handle this, the &lt;a href=&quot;http://www.creedictionary.com/converter/maskwacis.php&quot;&gt;Maskwacîs
Converter&lt;/a&gt; does not, instead producing &lt;strong&gt;ᐁ ᐁᐧᐸᐱᐦᑫᐁᐧᐱᓇᒪᐦᐠ&lt;/strong&gt;, with an
erroneous “ᐦᐠ” cluster at the end.&lt;/p&gt;

&lt;h3 id=&quot;transliterating-non-cree-words&quot;&gt;Transliterating non-Cree words&lt;/h3&gt;

&lt;p&gt;Some transliterators attempt to convert every Latin character, even if
it doesn’t make sense. Take the case of “Maskêkosihk Trail”—a road
that goes from Edmonton to &lt;a href=&quot;http://enochnation.ca/&quot;&gt;Enoch Cree Nation&lt;/a&gt;. The City of
Edmonton unveiled the street sign, and, in the process, they unveiled an
embarrassment:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/yasc/mayor-and-chief.JPG&quot; alt=&quot;Maskêkosihk trail rendered as &amp;quot;ᒪᐢᑫᑯᓯᐦᐠ  ᐟrᐊᐃl&amp;quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;small&gt; Image source: &lt;a href=&quot;https://www.cbc.ca/news/canada/edmonton/renamed-maskekosihk-trail-part-of-city-s-ongoing-reconciliation-commitment-1.3446162&quot;&gt;CBC&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Not only does the syllabics transliteration of the sign contain the “hk”
error as mentioned above, but it half-transliterates &lt;em&gt;the English word&lt;/em&gt;
“trail” into syllabics. The result is that “trail” is rendered as
“ᐟrᐊᐃl”, which &lt;em&gt;contains Latin characters in the transliteration!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In my opinion, an SRO to syllabics transliterator should refuse to
transliterate words that do not have the structure of a Cree word.
However, all three of the mentioned transliterators do attempt to
transliterate “trail” with differing results:&lt;sup id=&quot;fnref:2&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:2&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Maskwacîs Cree Dictionary&lt;/td&gt;
      &lt;td&gt;ᐟrᐊᐃl&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Algonquian Linguistic Atlas&lt;/td&gt;
      &lt;td&gt;ᐟᕒᐊᐃᐪ&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Syllabics.net&lt;/td&gt;
      &lt;td&gt;ᐟᕒᐊᐃᓬ&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;long-vowels&quot;&gt;Long vowels&lt;/h3&gt;

&lt;p&gt;Long vowels (âêîô) are distinct from short vowels (aio) in Cree. Long
vowels are written with a dot above in syllabics. The exception is for
“ê” because it is always long; as a result, some writers also drop the
diacritic when writing “e” in SRO as well. It’s important to differentiate
between long and short vowels, because it makes distinctions between
words. For example, nipiy/ᓂᐱᕀ means “water” while nîpiy/ᓃᐱᕀ means
“leaf”. However, there is such a thing as “plain” script, where the
vowel dots are omitted, and &lt;a href=&quot;https://en.wikipedia.org/wiki/Canadian_Aboriginal_syllabics#Pointing&quot;&gt;pointed&lt;/a&gt; script where the vowels have all
dots.&lt;/p&gt;

&lt;p&gt;Another complication is that the “standard” Roman orthography in
practice has multiple conventions for writing long vowels: using
a macron (◌̄) and using a circumflex (◌̂).&lt;sup id=&quot;fnref:3&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:3&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;

&lt;p&gt;How do the various converters handle long vowel diacritics? &lt;a href=&quot;http://www.creedictionary.com/converter/maskwacis.php&quot;&gt;The Maskwacîs
converter&lt;/a&gt; does not produce dots for long vowels at all, however it
accepts both macrons and circumflexes as input. The &lt;a href=&quot;https://syllabics.atlas-ling.ca/&quot;&gt;Algonquian Lingustic
Atlas’s converter&lt;/a&gt; not only produces dots, but supports input in
either macrons or circumflexes. The &lt;a href=&quot;http://www.syllabics.net/convert/plainscree&quot;&gt;syllabics.net converter&lt;/a&gt; does
worst of all, handling &lt;em&gt;only&lt;/em&gt; macrons for long vowels. It simply spits
out characters written with circumflexes. Additionally, it does not
handle “ê” without an diacritics, which all other converters do.&lt;/p&gt;

&lt;h3 id=&quot;other-odds-and-ends&quot;&gt;Other odds and ends&lt;/h3&gt;

&lt;p&gt;Other issues for syllabics converters include how they deal with dashes,
how they deal with combining diacritics, rather than pre-composed
characters, and whether they produce the correct Unicode characters for
the syllabics rather than very convincing look-alikes. There’s also the
&lt;a href=&quot;https://crk-orthography.readthedocs.io/en/stable/glossary.html#term-sandhi&quot;&gt;sandhi orthographic rule&lt;/a&gt;, but honestly, I’m not sure I fully
comprehend how to apply this rule myself.&lt;/p&gt;

&lt;h3 id=&quot;summary&quot;&gt;Summary&lt;/h3&gt;

&lt;p&gt;Here’s a breakdown of the previous issues, and whether each
transliterator can handle it correctly.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: left&quot;&gt; &lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Word-final “hk”&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Non-Cree words&lt;/th&gt;
      &lt;th style=&quot;text-align: right&quot;&gt;Long vowels&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Maskwacîs Cree Dictionary&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;❌&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;❌&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Algonquian Linguistic Atlas&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;✅&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;❌&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;✅&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: left&quot;&gt;Syllabics.net&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;✅&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;❌&lt;/td&gt;
      &lt;td style=&quot;text-align: right&quot;&gt;❌&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h2 id=&quot;wheres-the-source-code&quot;&gt;Where’s the source code?&lt;/h2&gt;

&lt;p&gt;The most pressing issue to me personally is that I cannot find source
code for any of these converters! This means that if other people  want
to incorporate a converter into their own app without an active internet
connection, they can’t. They have to either reverse-engineer the
converters online, or write their own code to do the conversion.&lt;/p&gt;

&lt;h2 id=&quot;cree-sro-syllabics-an-open-source-python-and-javascript-library-for-syllabics-conversion&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cree-sro-syllabics&lt;/code&gt;: an open-source Python and JavaScript library for syllabics conversion&lt;/h2&gt;

&lt;p&gt;My solution was to create a &lt;a href=&quot;https://pypi.org/project/cree-sro-syllabics/&quot;&gt;Python library&lt;/a&gt; that is &lt;strong&gt;free and
open source&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: (&lt;em&gt;2018-12-06&lt;/em&gt;) &lt;a href=&quot;https://www.npmjs.com/package/cree-sro-syllabics&quot;&gt;Now also available for JavaScript&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;It handles all the issues previously mentioned. Try it with the
following test cases:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://syllabics.app/#!sro:Maskekosihk%20trail&quot;&gt;Maskekosihk trail&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://syllabics.app/#!sro:êwêpâpîhkêwêpinamahk&quot;&gt;êwêpâpîhkêwêpinamahk&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://syllabics.app/#!sro:ēwēpâpīhkēwēpinamahk&quot;&gt;ēwēpâpīhkēwēpinamahk&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://syllabics.app/#!sro:ewepapihkewepinamahk&quot;&gt;ewepapihkewepinamahk&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;!--
It can also convert from syllabics to SRO:

 - [ᒣᕒᐃᕀ](https://syllabics.app/#!syl:ᒣᕒᐃᕀ)
 - [ᐁᑯᓯ](https://syllabics.app/#!syl:ᐁᑯᓯ)

--&gt;

&lt;p&gt;The source code for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cree-sro-syllabics&lt;/code&gt; can be found on its &lt;a href=&quot;https://github.com/eddieantonio/cree-sro-syllabics&quot;&gt;GitHub
page&lt;/a&gt;, but it can also be seamlessly incorporated into a Python
project that uses &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt; by installing it with:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install cree-sro-syllabics
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: (&lt;em&gt;2018-12-06&lt;/em&gt;) You can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;npm&lt;/code&gt; to install
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cree-sro-syllabics&lt;/code&gt; in your JavaScript project:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;npm install cree-sro-syllabics --save
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Or you can &lt;a href=&quot;https://raw.githubusercontent.com/eddieantonio/cree-sro-syllabics.js/master/cree-sro-syllabics.js&quot;&gt;copy-paste the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.js&lt;/code&gt; file&lt;/a&gt; to your project (as long as you
keep the AGPL license comment at the top!)&lt;/p&gt;

&lt;h2 id=&quot;the-future&quot;&gt;The future&lt;/h2&gt;

&lt;p&gt;I hope in future versions to add support for Woods Cree and other West
Cree dialects. There are also a few interesting things that can be done
to make sure SRO and syllabics conversions can be completely reversed
without losing information about morpheme boundaries.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: (&lt;em&gt;2018-12-06&lt;/em&gt;) In the original post, the library was called
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;crk_orthography&lt;/code&gt;. It has been renamed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cree-sro-syllabics&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT 2&lt;/strong&gt;: (&lt;em&gt;2018-12-06&lt;/em&gt;) &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cree-sro-syllabics&lt;/code&gt; now features beta
support for Woods Cree (Th-dialect) and Swampy Cree (N-dialect).&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I don’t have an iPhone to confirm this, but I believe this is the
same converter bundled in the &lt;a href=&quot;http://www.creedictionary.com/software/index.php&quot;&gt;Cree Dictionary
app&lt;/a&gt;. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:2&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;I wonder if the sign designer used the Maskwacîs transliterator to
get this result. &lt;a href=&quot;#fnref:2&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id=&quot;fn:3&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;Anecdotally, I find that most writers near Edmonton and Maskwacîs
prefer circumflexes to macrons; however noted Algonquian linguist
Arok Wolvengrey prefers macrons. Heck, Jean Okimāsis writes her
surname with a macron! &lt;a href=&quot;#fnref:3&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><summary type="html"></summary></entry><entry><title type="html">Syntax and Sensibility: Using language models to detect and correct syntax errors</title><link href="https://eddieantonio.ca/blog/2018/01/15/sensibility/" rel="alternate" type="text/html" title="Syntax and Sensibility: Using language models to detect and correct syntax errors" /><published>2018-01-15T00:00:00+00:00</published><updated>2018-01-15T00:00:00+00:00</updated><id>https://eddieantonio.ca/blog/2018/01/15/sensibility</id><content type="html" xml:base="https://eddieantonio.ca/blog/2018/01/15/sensibility/">&lt;p&gt;Has this ever happened to you?&lt;/p&gt;

&lt;p&gt;You’re writing source code, say this:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ca.ualberta.cs.example&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Hello&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;exitstatus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;not enough args&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;exit&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exitstatus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;system&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;out&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;hello, world!&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And when you go to compile it, you’re bombarded with error messages:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Hello.java:6: error: variable declaration not allowed here
            int exitstatus = 2;
                ^
Hello.java:10: error: &amp;lt;identifier&amp;gt; expected
        system.out.println(&quot;hello, world!&quot;);
                          ^
Hello.java:10: error: illegal start of type
        system.out.println(&quot;hello, world!&quot;);
                           ^
Hello.java:12: error: class, interface, or enum expected
}
^
4 errors
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Four error messages! So the error must be on at least &lt;strong&gt;line 6&lt;/strong&gt;, right?
Right…?&lt;/p&gt;

&lt;p&gt;After scratching your head for a while, you figure out that &lt;strong&gt;line
5&lt;/strong&gt;, which looks like this:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;…should actually have an open brace at the end of the line, like this:&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;args&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;How the crumb was I supposed to figure &lt;em&gt;that&lt;/em&gt; out from pouring over the
error messages?&lt;/p&gt;

&lt;h1 id=&quot;theres-got-to-be-a-better-way&quot;&gt;There’s got to be a better way&lt;/h1&gt;

&lt;p&gt;Introducing &lt;em&gt;&lt;a href=&quot;https://github.com/naturalness/sensibility/tree/saner2018&quot;&gt;Sensibility&lt;/a&gt;&lt;/em&gt;, a tool designed to not only &lt;strong&gt;find&lt;/strong&gt; where
these syntax errors are, but also to tell you how you might be able to
&lt;strong&gt;fix&lt;/strong&gt; it!&lt;/p&gt;

&lt;p&gt;The secret: use natural language models—specifically &lt;em&gt;n&lt;/em&gt;-gram and LSTM
language models—to figure out where the “unnatural” code lurks.&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;But Eddie… using NLP on code? That’s craziness!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nope! It’s &lt;a href=&quot;https://cacm.acm.org/magazines/2016/5/201595-on-the-naturalness-of-software/fulltext&quot;&gt;&lt;em&gt;naturalness&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sensibility&lt;/em&gt; can produce a valid fix for about 50% of all single-token
syntax errors, often producing the &lt;em&gt;true&lt;/em&gt; fix that a novice programmer
would have made. For example, given the broken code above, &lt;em&gt;Sensibility&lt;/em&gt;
produces this output:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Hello.java:5:29: try inserting '{'
        if (args.length != 2)
                               ^
                               {
Hello.java:6:13: try replacing int with {
            int exitStatus = 2;
            ^
            {
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Either of which will fix the file, but the former is probably what you
intended :).&lt;/p&gt;

&lt;p&gt;For more details, read our paper &lt;em&gt;Syntax and Sensibility: Using language
models to detect and correct syntax errors&lt;/em&gt; to appear in &lt;a href=&quot;http://saner.unimol.it/&quot;&gt;SANER
2018&lt;/a&gt;. &lt;a href=&quot;http://softwareprocess.es/pubs/santos2018SANER-syntax.pdf&quot;&gt;Here’s a link to a
preprint&lt;/a&gt;.
And here’s the BibTeX.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;@inproceedings{eddie2018SANER2018sasulmtdacse,
 accepted = {2017-12-18},
 author = {Eddie Antonio Santos and Joshua Charles Campbell and Dhvani Patel and Abram Hindle and José Nelson Amaral},
 authors = {Eddie Antonio Santos, Joshua Charles Campbell, Dhvani Patel, Abram Hindle, José Nelson Amaral},
 booktitle = {25th IEEE International Conference on Software Analysis, Evolution, and Reengineering (SANER 2018)},
 date = {2018-04-21},
 funding = {NSERC Discovery, MITACS Accelerate},
 location = {Campobasso, Italy},
 pagerange = {1--11},
 pages = {1--11},
 role = { Author},
 title = {Syntax and Sensibility: Using language models to detect and correct syntax errors},
 type = {inproceedings},
 url = {http://softwareprocess.ca/pubs/santos2018SANER-syntax.pdf},
 venue = {25th IEEE International Conference on Software Analysis, Evolution, and Reengineering (SANER 2018)},
 year = {2018}
}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/naturalness/sensibility/tree/saner2018&quot;&gt;The code is available on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Special thanks to my coauthors, Joshua Charles Campbell, Dhvani Patel,
&lt;a href=&quot;http://softwareprocess.es/&quot;&gt;Abram Hindle&lt;/a&gt; and &lt;a href=&quot;http://www.cs.ualberta.ca/~amaral&quot;&gt;José Nelson
Amaral&lt;/a&gt;.&lt;/p&gt;</content><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><summary type="html">Has this ever happened to you?</summary></entry><entry><title type="html">Anatomy of a crash repository</title><link href="https://eddieantonio.ca/blog/2016/11/18/anatomy-of-a-crash-repository/" rel="alternate" type="text/html" title="Anatomy of a crash repository" /><published>2016-11-18T00:00:00+00:00</published><updated>2016-11-18T00:00:00+00:00</updated><id>https://eddieantonio.ca/blog/2016/11/18/anatomy-of-a-crash-repository</id><content type="html" xml:base="https://eddieantonio.ca/blog/2016/11/18/anatomy-of-a-crash-repository/">&lt;p&gt;What mysteries sleep deep within Ubuntu’s crash report repository?
Find out in our latest preprint, &lt;strong&gt;&lt;a href=&quot;https://peerj.com/preprints/2601/?td=bl&quot;&gt;Anatomy of a crash repository&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ever wonder what functions crash most often? What signals cause the
most crashes (Spoilers: &lt;span class=&quot;spoilers&quot;&gt; it’s &lt;a href=&quot;https://en.wikipedia.org/wiki/Segmentation_fault&quot;&gt;segfaults&lt;/a&gt;&lt;/span&gt;)? Then
you should read our preprint!&lt;/p&gt;

&lt;p&gt;This is a follow-up &lt;a href=&quot;/2016/02/06/the-unreasonable-effectiveness/&quot;&gt;my previous paper&lt;/a&gt; with Joshua
Campbell and our supervisor Abram Hindle.&lt;/p&gt;</content><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><summary type="html">What mysteries sleep deep within Ubuntu’s crash report repository? Find out in our latest preprint, Anatomy of a crash repository!</summary></entry><entry><title type="html">Judging a Commit by Its Cover</title><link href="https://eddieantonio.ca/blog/2016/02/22/judging-a-commit-by-its-cover/" rel="alternate" type="text/html" title="Judging a Commit by Its Cover" /><published>2016-02-22T00:00:00+00:00</published><updated>2016-02-22T00:00:00+00:00</updated><id>https://eddieantonio.ca/blog/2016/02/22/judging-a-commit-by-its-cover</id><content type="html" xml:base="https://eddieantonio.ca/blog/2016/02/22/judging-a-commit-by-its-cover/">&lt;p&gt;For the &lt;a href=&quot;http://2016.msrconf.org/#/challenge&quot;&gt;MSR 2016 Challenge&lt;/a&gt;, I applied &lt;strong&gt;data science&lt;/strong&gt; to test
a gut feeling I had: commits with weird, unusual log messages are often
committing lower quality than code than boring, ordinary commit messages.&lt;/p&gt;

&lt;p&gt;So: Are commits that &lt;em&gt;look&lt;/em&gt; fishy…&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/orezpraw/unnaturalcode/commit/7c15e369fe58b1537141eb31f28f549a01d10380&quot;&gt;&lt;img src=&quot;/blog/images/judging-commits/josh-commit.png&quot; alt=&quot;Commit message: &amp;quot;Maybe now it's working?&amp;quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;…actually hiding dubious code?&lt;/p&gt;

&lt;p&gt;Is my hunch true? (Spoilers: &lt;span class=&quot;spoilers&quot;&gt;Marginally&lt;/span&gt;).&lt;/p&gt;

&lt;p&gt;To test, we correlated build status from &lt;a href=&quot;https://travis-ci.org/&quot;&gt;Travis-CI&lt;/a&gt; with &lt;a href=&quot;https://en.wikipedia.org/wiki/N-gram#n-gram_models&quot;&gt;&lt;em&gt;n&lt;/em&gt;-gram
language models&lt;/a&gt; on commit messages. Here’s the abstract:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Developers summarize their changes to code in commit messages. When a message seems “unusual,” however, this puts doubt into the quality of the code contained in the commit. We trained
n-gram language models and used cross-entropy as an indicator of commit message “unusualness” of over 120 000 commits from open source projects. Build statuses collected from Travis-CI were used as a proxy for code quality. We then compared the distributions of failed and successful commits with regards to the “unusualness” of their commit message. Our analysis yielded significant results when correlating cross-entropy with build status.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;iframe width=&quot;560&quot; height=&quot;315&quot; src=&quot;https://www.youtube.com/embed/UPQlIbwksgw&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Intrigued? &lt;a href=&quot;https://peerj.com/preprints/1771/?td=bl&quot;&gt;Read the preprint!&lt;/a&gt; (And fork the &lt;a href=&quot;https://github.com/eddieantonio/judging-commits&quot;&gt;replication
code&lt;/a&gt; and &lt;a href=&quot;https://drive.google.com/open?id=0ByMXxDHxG3WSbzEtc1BoTk1NcTA&quot;&gt;data&lt;/a&gt;!)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EDIT&lt;/strong&gt;: Added my presentation.&lt;/p&gt;

&lt;p&gt;Acknowledgements to my supervisor, &lt;a href=&quot;http://softwareprocess.es&quot;&gt;Abram Hindle&lt;/a&gt;, and my colleagues &lt;a href=&quot;http://dl.acm.org/author_page.cfm?id=88158695357&amp;amp;coll=DL&amp;amp;dl=ACM&amp;amp;trk=0&amp;amp;cfid=583931408&amp;amp;cftoken=65482945&quot;&gt;S.
Kalen Romansky&lt;/a&gt;, and &lt;a href=&quot;https://sites.google.com/site/shaifulhome/home&quot;&gt;Shaiful Chowdhury&lt;/a&gt; for their
reviews and comments!&lt;/p&gt;</content><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><summary type="html">For the MSR 2016 Challenge, I applied data science to test a gut feeling I had: commits with weird, unusual log messages are often committing lower quality than code than boring, ordinary commit messages.</summary></entry><entry><title type="html">The Unreasonable Effectiveness of Traditional Information Retrieval in Crash Report Deduplication</title><link href="https://eddieantonio.ca/blog/2016/02/06/the-unreasonable-effectiveness/" rel="alternate" type="text/html" title="The Unreasonable Effectiveness of Traditional Information Retrieval in Crash Report Deduplication" /><published>2016-02-06T00:00:00+00:00</published><updated>2016-02-06T00:00:00+00:00</updated><id>https://eddieantonio.ca/blog/2016/02/06/the-unreasonable-effectiveness</id><content type="html" xml:base="https://eddieantonio.ca/blog/2016/02/06/the-unreasonable-effectiveness/">&lt;p&gt;Last week, &lt;a href=&quot;http://jcc.space/&quot;&gt;Joshua Charles Campbell&lt;/a&gt;, &lt;a href=&quot;http://softwareprocess.es/&quot;&gt;Abram Hindle&lt;/a&gt;, and
I wrote a paper on crash report bucketing.&lt;/p&gt;

&lt;p&gt;You know when a program crashes, and it asks you to “Send a report,” so
you just click “Okay” to make the message go away? This paper addresses
the other side of that story, where several thousands of people dismissing that
message with an innocent “Okay” every single day leads to a veritable deluge of
crash reports that leave the developers with more reports than they know
how to deal with.&lt;/p&gt;

&lt;p&gt;We developed and evaluated a technique called &lt;a href=&quot;https://github.com/orezpraw/partycrasher&quot;&gt;PartyCrasher&lt;/a&gt; that’s
like Google for crash reports. Basically, we get the thousands of
crashes, do some very basic text search, and find a “bucket” of crashes
that look most like it.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/blog/images/unreasonable/crashbuckit.svg&quot; alt=&quot;Check out the crash in the bucket&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Here’s the official abstract:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;Organizations like Mozilla, Microsoft, and Apple are flooded with
thousands of automated crash reports per day. Although crash reports
contain valuable information for debugging, there are often too many
for developers to examine individually. Therefore, in industry, crash
reports are often automatically grouped together in buckets. Ubuntu’s
repository contains crashes from hundreds of software systems
available with Ubuntu. A variety of crash report bucketing methods are
evaluated using data collected by Ubuntu’s Apport automated crash
reporting system. The trade-off between precision and recall of
numerous scalable crash 7 deduplication techniques is explored. A set
of criteria that a crash deduplication method must meet is presented
and several methods that meet these criteria are evaluated on a new
dataset. The evaluations presented in this paper show that using
off-the-shelf information retrieval techniques, that were not designed
to be used with crash reports, outperform other techniques which are
specifically designed for the task of crash bucketing at realistic
industrial scales. This research indicates that automated crash
bucketing still has a lot of room for improvement, especially in terms
of identifier tokenization.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Still interested? &lt;a href=&quot;https://peerj.com/preprints/1705/?td=bl&quot;&gt;Read the preprint!&lt;/a&gt;&lt;/p&gt;</content><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><summary type="html">Last week, Joshua Charles Campbell, Abram Hindle, and I wrote a paper on crash report bucketing.</summary></entry><entry><title type="html">Authoring man pages in Markdown with Pandoc</title><link href="https://eddieantonio.ca/blog/2015/12/18/authoring-manpages-in-markdown-with-pandoc/" rel="alternate" type="text/html" title="Authoring man pages in Markdown with Pandoc" /><published>2015-12-18T00:00:00+00:00</published><updated>2015-12-18T00:00:00+00:00</updated><id>https://eddieantonio.ca/blog/2015/12/18/authoring-manpages-in-markdown-with-pandoc</id><content type="html" xml:base="https://eddieantonio.ca/blog/2015/12/18/authoring-manpages-in-markdown-with-pandoc/">&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Man_page&quot;&gt;man pages&lt;/a&gt; are one of the most tenacious forms of
programmer-oriented documentation. These &lt;em&gt;manual pages&lt;/em&gt; have changed
relatively little since their &lt;a href=&quot;https://www.bell-labs.com/usr/dmr/www/manintro.html&quot;&gt;first publication in 1971&lt;/a&gt;.
Regardless of whether or not they’re still relevant in the age of &lt;em&gt;the
internet&lt;/em&gt;, they remain an invaluable reference for any serious command
line application or C library function.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://media2.giphy.com/media/WQD6NEEsVTvxK/giphy.gif&quot; alt=&quot;Giphy&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Yet, in the whimsical technological wonderland that is 2015, one often
wonders: there has &lt;em&gt;got&lt;/em&gt; to be a better way of authoring man pages than
in their native form: crusty old &lt;a href=&quot;https://en.wikipedia.org/wiki/Troff&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;troff&lt;/code&gt;&lt;/a&gt; macros–document
publishing’s equivalent of assembly language. Alas, dear reader, there is
a way! It’s called &lt;a href=&quot;http://pandoc.org/&quot;&gt;Pandoc&lt;/a&gt;: a self-touted “universal document
converter”. For our purposes, it can translate a modern expressive
document formatting language such as Markdown into a troff-formated man
page.&lt;/p&gt;

&lt;p&gt;So let’s get started!&lt;/p&gt;

&lt;p&gt;First, install Pandoc somehow. I recommend using &lt;a href=&quot;https://www.haskell.org/cabal/&quot;&gt;cabal&lt;/a&gt; if you plan
on writing your own Pandoc filters in Haskell. Sane people can probably
get by just fine installing Pandoc using their favourite package
manager, or following the instructions on &lt;a href=&quot;http://pandoc.org/installing.html&quot;&gt;Pandoc’s website&lt;/a&gt;.&lt;/p&gt;

&lt;h1 id=&quot;the-markdown-template&quot;&gt;The Markdown template&lt;/h1&gt;

&lt;p&gt;Although you may write your man page in any input format that Pandoc
understands, I use Markdown since Pandoc provides a few Markdown
extensions that are quite useful for man page authoring. Here’s the
general template I use (&lt;a href=&quot;https://gist.githubusercontent.com/eddieantonio/55752dd76a003fefb562/raw/38f6eb9de250feef22ff80da124b0f439fba432d/hello.1.md&quot;&gt;view its raw Markdown source text&lt;/a&gt;):&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/eddieantonio/55752dd76a003fefb562.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;There are a few things to note:&lt;/p&gt;

&lt;h2 id=&quot;headers&quot;&gt;Headers&lt;/h2&gt;

&lt;p&gt;man pages generally have at least the following sections:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;NAME&lt;/strong&gt; — The name of the command or function and a minimal
description.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SYNOPSIS&lt;/strong&gt; — A terse listing of command line arguments or the
function prototype.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;DESCRIPTION&lt;/strong&gt; — The bulk of the man page; describes in-detail how
to use the command, or function.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;man pages also tend to include some of the following sections:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;OPTIONS&lt;/strong&gt; — Command line options; this may also be a subsection of
&lt;strong&gt;DESCRIPTION&lt;/strong&gt;.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;EXAMPLES&lt;/strong&gt; — Informative usage examples. Personally, I prefer man
pages that list the most common use cases in the &lt;strong&gt;DESCRIPTION&lt;/strong&gt;, but
it’s also handy to have a section purely dedicated to example usage.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FILES&lt;/strong&gt; — Descriptions names of important files, such as
configuration files.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;ENVIRONMENT&lt;/strong&gt; — Description of relevant environment variables that affect
functionality.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;BUGS&lt;/strong&gt; — well-known bugs; however, I generally just throw a link to
the corresponding issue tracker.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;AUTHORS&lt;/strong&gt; — Who made it?&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;SEE ALSO&lt;/strong&gt; — References to other man pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another useful resource on these headings is &lt;a href=&quot;http://www.tldp.org/HOWTO/Man-Page/q3.html&quot;&gt;TLDP’s man page
HOW-TO&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;section-numbers&quot;&gt;Section numbers&lt;/h2&gt;

&lt;p&gt;All manual pages reside in a numbered section. A man page is often
referenced by its name and section number as in &lt;strong&gt;hello(1)&lt;/strong&gt;. A list
of section numbers can be found in &lt;a href=&quot;http://linux.die.net/man/1/man&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man&lt;/code&gt;’s man page&lt;/a&gt;, but some
more common sections are section 1, general user commands; and section
3, C library functions.&lt;/p&gt;

&lt;h2 id=&quot;the-filename&quot;&gt;The filename&lt;/h2&gt;

&lt;p&gt;For this file, I’ve chosen &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello.1.md&lt;/code&gt;. The filename ends with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.md&lt;/code&gt; to
indicate it’s a Markdown document. Since its ultimate goal is to become
a man page called &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello.1&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.md&lt;/code&gt; is prefixed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.1&lt;/code&gt;. &lt;strong&gt;Do not
mistake &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.1&lt;/code&gt; for a file extension!&lt;/strong&gt; It’s just convention that a man
page’s “file extension” is the manual section number it belongs to.&lt;/p&gt;

&lt;p&gt;In order for man pages to be generally accessible, they must be
stored in a path matching this pattern:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;$PREFIX&lt;/span&gt;/share/man/man&lt;span class=&quot;nv&quot;&gt;$SECTION&lt;/span&gt;/&lt;span class=&quot;nv&quot;&gt;$NAME&lt;/span&gt;.&lt;span class=&quot;nv&quot;&gt;$SECTION&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$NAME&lt;/code&gt; is the name of the man page (i.e., you will type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;man
$NAME&lt;/code&gt; to view it); &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$SECTION&lt;/code&gt; is the section number of the man page;
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;$PREFIX&lt;/code&gt; is a prefix to install tools in Unix such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/local&lt;/code&gt;
or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/opt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For example, my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt; binary will live in the following path:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/usr/local/bin/hello
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Its corresponding man page will live beside it here:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;/usr/local/share/man/man1/hello.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;the-metadata-block&quot;&gt;The metadata block&lt;/h2&gt;

&lt;p&gt;Pandoc’s Markdown parses the first three lines as metadata &lt;a href=&quot;http://pandoc.org/README.html#metadata-blocks&quot;&gt;if they are
prefixed with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%&lt;/code&gt; sign&lt;/a&gt;. The first line is special:
it’s the document title, and for man pages in particular, it has
the following format:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% NAME(#) Version | Header
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case, we have a command named &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello&lt;/code&gt;. It’s a general command,
so its manual section is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1&lt;/code&gt;. It’s the very first major version of this
command, so it gets &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Version 1.0&lt;/code&gt;. Finally, the header–separated from
the rest of the title by a pipe character (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;|&lt;/code&gt;)–usually indicates
what set of documentation this manual page belongs to.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;% HELLO(1) Version 1.0 | Frivolous &quot;Hello World&quot; Documentation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If we provided author names using the second metadata line, Pandoc would
automatically append an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;AUTHORS&lt;/code&gt; section to the very end of the man
page. I have opted to write this section by hand, however, for greater
control over the formatting of this section.&lt;/p&gt;

&lt;h2 id=&quot;useful-pandoc-markdown-extensions&quot;&gt;Useful Pandoc Markdown Extensions&lt;/h2&gt;

&lt;p&gt;I have found two Pandoc Markdown extensions particularly useful for man
page authoring: &lt;a href=&quot;http://pandoc.org/README.html#definition-lists&quot;&gt;definition lists&lt;/a&gt; and &lt;a href=&quot;http://pandoc.org/README.html#line-blocks&quot;&gt;line blocks&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Definition lists are useful for describing particular command line
arguments, files, and environment variables in depth.&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;-o, --output &lt;span class=&quot;ge&quot;&gt;*file*&lt;/span&gt;

:  The --out option changes the default output to the specified file...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Line blocks, which preserve leading spaces and line breaks, are useful
for writing multi-line synopses. Often, multiple lines are needed if
there are several mutually-exclusive invocations (such as using the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--help&lt;/code&gt; option in most commands).&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;| &lt;span class=&quot;gs&quot;&gt;**hello**&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\[&lt;/span&gt;&lt;span class=&quot;gs&quot;&gt;**-o**&lt;/span&gt;|&lt;span class=&quot;gs&quot;&gt;**--out**&lt;/span&gt; _file_] &lt;span class=&quot;se&quot;&gt;\[&lt;/span&gt;&lt;span class=&quot;ge&quot;&gt;_dedication_&lt;/span&gt;]
| &lt;span class=&quot;gs&quot;&gt;**hello**&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\[&lt;/span&gt;&lt;span class=&quot;gs&quot;&gt;**-v**&lt;/span&gt;|&lt;span class=&quot;gs&quot;&gt;**--version**&lt;/span&gt;]
| &lt;span class=&quot;gs&quot;&gt;**hello**&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\[&lt;/span&gt;&lt;span class=&quot;gs&quot;&gt;**-h**&lt;/span&gt;|&lt;span class=&quot;gs&quot;&gt;**--help**&lt;/span&gt;]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note the escaped square brackets (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;\[ ... ]&lt;/code&gt;). Since line blocks still
contain formatted text–only initial spacing and line-breaking are
preserved–any Markdown syntax such as link brackets are still
interpreted. This is useful for marking individual options with bold
text and filenames with italics. However, this gets in the way when
using idiomatic square brackets to denote optional components. The
opening square bracket must be escaped to avoid text being interpreted
as link text.&lt;/p&gt;

&lt;h1 id=&quot;building-the-man-page&quot;&gt;Building the man page&lt;/h1&gt;

&lt;p&gt;Now, to turn our Markdown into an actual man page, we just run this
command:&lt;/p&gt;

&lt;div class=&quot;language-sh highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pandoc &lt;span class=&quot;nt&quot;&gt;--standalone&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--to&lt;/span&gt; man hello.1.md &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; hello.1
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--standalone&lt;/code&gt; option is necessary; without it, Pandoc will
naïvely translate the Markdown to troff without providing the header,
footer, or the general man page structure to the output.&lt;/p&gt;

&lt;h1 id=&quot;building-with-a-makefile&quot;&gt;Building with a Makefile&lt;/h1&gt;

&lt;p&gt;I often create man pages in conjunction with a simple &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;make&lt;/code&gt;
build system. The following is a file you can copy paste or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;include&lt;/code&gt;
into your Makefile to make it convert any Markdown-formatted man page
into appropriate troff output.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/b8fee891304be526a5c1.js?file=pandoc-man.mk&quot;&gt; &lt;/script&gt;

&lt;p&gt;To use, simply include &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pandoc-man.mk&lt;/code&gt; in your Makefile, set
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;MANSECTION&lt;/code&gt; as appropriate for your man page (unnecessary if it’s in
section 1), and declare the name of your desired man page as a build
dependency. For my &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello(1)&lt;/code&gt; example, my Makefile is as follows:&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/b8fee891304be526a5c1.js?file=Makefile&quot;&gt; &lt;/script&gt;

&lt;h1 id=&quot;useful-examples&quot;&gt;Useful Examples&lt;/h1&gt;

&lt;p&gt;I have authored a few man pages this way:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/eddieantonio/imgcat/blob/master/doc/imgcat.1.md&quot;&gt;imgcat(1)&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/eddieantonio/license/blob/master/license.1.md&quot;&gt;license(1)&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/eddieantonio/unormalize/blob/master/man/unormalize.1.md&quot;&gt;unormalize(1)&lt;/a&gt;&lt;/strong&gt;, and its friends &lt;strong&gt;nfc(1)&lt;/strong&gt;,
&lt;strong&gt;nfd(1)&lt;/strong&gt;, &lt;strong&gt;nfkc(1)&lt;/strong&gt;, &lt;strong&gt;nfkd(1)&lt;/strong&gt; (which all point to the same man
page by way of symbolic links).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As well, it’s useful to look at other well-written man pages for
inspiration. For example, &lt;strong&gt;&lt;a href=&quot;http://curl.haxx.se/docs/manpage.html&quot;&gt;curl(1)&lt;/a&gt;&lt;/strong&gt; is particularly good.&lt;/p&gt;</content><author><name>Eddie Antonio Santos</name><uri>https://eddieantonio.ca/</uri></author><summary type="html">man pages are one of the most tenacious forms of programmer-oriented documentation. These manual pages have changed relatively little since their first publication in 1971. Regardless of whether or not they’re still relevant in the age of the internet, they remain an invaluable reference for any serious command line application or C library function.</summary></entry></feed>