<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://hemann.pl/feed.xml" rel="self" type="application/atom+xml" /><link href="https://hemann.pl/" rel="alternate" type="text/html" /><updated>2026-09-01T21:26:05+00:00</updated><id>https://hemann.pl/feed.xml</id><title type="html">Jason Hemann</title><subtitle>Personal Site.</subtitle><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><entry><title type="html">Can Emacs Mac Port Have It All? Multi-ttwhy?</title><link href="https://hemann.pl/multi-ttwhy/" rel="alternate" type="text/html" title="Can Emacs Mac Port Have It All? Multi-ttwhy?" /><published>2026-09-01T00:00:00+00:00</published><updated>2026-09-01T00:00:00+00:00</updated><id>https://hemann.pl/multi-ttwhy</id><content type="html" xml:base="https://hemann.pl/multi-ttwhy/"><![CDATA[<p>I want to have just one warm Emacs process. From it, I want to open a
graphical frame when working in my OS GUI, open a text frame from a
terminal or over SSH, and have both be views onto the same buffers and
the same Lisp world. This does not seem like a big ask. Emacs has a
server; <code class="language-plaintext highlighter-rouge">emacsclient</code> has flags for exactly this:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>emacs <span class="nt">--daemon</span>
emacsclient <span class="nt">-c</span>             <span class="c"># a new graphical frame</span>
emacsclient <span class="nt">-t</span>             <span class="c"># a new frame on this terminal</span>
</code></pre></div></div>

<p>Easy peasy. And, once the server is warm, lickety-split.</p>

<p>Except that this combination does not work in an unpatched Emacs Mac
Port. GNU Emacs includes its official backend. Emacs Mac Port is a
separate port with its own collection of excellent macOS integrations
and its own <code class="language-plaintext highlighter-rouge">mac</code> display backend. On a mac it should be the obvious
choice. However, in Mac Port as it stands, GUI-first startup redirects
<code class="language-plaintext highlighter-rouge">-t</code> to a graphical frame, while daemon-first startup makes <code class="language-plaintext highlighter-rouge">-c</code> fall
back to a terminal frame. 0/2.</p>

<p>The <a href="https://github.com/jdtsmith/emacs-mac/blob/emacs-mac-30_1_exp/README-mac#L209-L212">current Mac Port source still says</a>
that it does not support multi-tty together with its GUI. TTY-only
multi-tty is supposed to work.</p>

<p>My current workaround was to stop using the Mac Port. I gave up the
Mac Port-specific features, but I do get the client/server model I was
trying to configure. But there is hope on the horizon.</p>

<h2 id="the-failure-is-asymmetric">The failure is asymmetric</h2>

<p>First, an Emacs vocabulary lesson. A graphical window containing Emacs
is a <em>frame</em>. An Emacs <em>window</em> is one of the <em>panes</em> inside it. A
terminal screen occupied by Emacs is also a frame, on a different
terminal. <em>Multi-tty</em> is Emacs’s ability to attach one running Emacs
process to multiple terminals at the same time. Each terminal gets its
own Emacs frame, while all the frames share the same buffers. A
graphical frame and a text frame belonging to one Emacs process
are one particular instance of this.</p>

<p>One Emacs process is designed to use <a href="https://www.gnu.org/software/emacs/manual/html_node/elisp/Frames.html">graphical and text terminals
simultaneously</a>.
The documented jobs of <a href="https://www.gnu.org/software/emacs/manual/html_node/emacs/emacsclient-Options.html"><code class="language-plaintext highlighter-rouge">emacsclient -c</code> and <code class="language-plaintext highlighter-rouge">emacsclient
-t</code></a>
are to ask that process for a graphical or text frame, respectively.
This is the general Emacs model.</p>

<p>So there are two kinds of failures in current release Mac Port:</p>

<ul>
  <li>Start the GUI first and call <code class="language-plaintext highlighter-rouge">server-start</code>: graphical client frames
work, but <code class="language-plaintext highlighter-rouge">emacsclient -t</code> is instead redirected to a graphical frame.</li>
  <li>Start a frame-less <code class="language-plaintext highlighter-rouge">--daemon</code> first: the process cannot initialize
the Mac GUI later, so <code class="language-plaintext highlighter-rouge">emacsclient -c</code> cannot make its first
graphical frame. When <code class="language-plaintext highlighter-rouge">-c</code> cannot make a graphical frame,
<code class="language-plaintext highlighter-rouge">emacsclient</code> deliberately falls back to a terminal frame, and so it
looks like the client is just ignoring our request.</li>
</ul>

<p>Both of these symptoms are described in the still-open
<a href="https://github.com/railwaycat/homebrew-emacsmacport/issues/52">issue #52</a>.</p>

<h2 id="a-partial-fix-ships-downstream">A partial fix ships downstream</h2>

<p>Railwaycat’s Homebrew builds apply a downstream multi-tty patch in the
current formulas for
<a href="https://github.com/railwaycat/homebrew-emacsmacport/blob/master/Formula/emacs-mac%4029.rb#L26-L33">Emacs Mac 29</a>,
<a href="https://github.com/railwaycat/homebrew-emacsmacport/blob/master/Formula/emacs-mac%4030exp.rb#L26-L33">30 experimental</a>,
and <a href="https://github.com/railwaycat/homebrew-emacsmacport/blob/master/Formula/emacs-mac%4031exp.rb#L48-L55">31 experimental</a>.</p>

<p>That patch fixes the GUI-first half. Start the Mac Port as an ordinary
graphical application, run <code class="language-plaintext highlighter-rouge">server-start</code>, and the same process can
serve both GUI and TTY clients. Users of Railwaycat’s working Homebrew
builds do not have to apply it by hand. As of September 1, 2026, however,
the 31 experimental formula fails to install because that patch no
longer applies cleanly after upstream branch changes; the failure is
tracked in <a href="https://github.com/railwaycat/homebrew-emacsmacport/issues/420">issue #420</a>.</p>

<p>It does not fix the daemon-first half. A true Mac Port daemon still
cannot initialize its first graphical display. And deleting the last
graphical frame while a TTY frame remains can leave the Mac application
in a bad state. The established
<a href="https://github.com/DarwinAwardWinner/mac-pseudo-daemon"><code class="language-plaintext highlighter-rouge">mac-pseudo-daemon</code></a>
workaround handles that lifecycle problem by starting graphically and
keeping a hidden GUI frame alive. It is a useful pseudo-daemon, but the
“pseudo” is doing real work in that name.</p>

<h2 id="good-news-everyonea-full-fix">Good news everyone—a full fix?</h2>

<p>Here is a delightful bit of news. On August 30, 2026, someone opened
<a href="https://github.com/jdtsmith/emacs-mac/pull/143">Mac Port pull request #143</a>,
“mac: support GUI and TTY frames from daemons”. It removes the remaining
mixed-frame blockers, makes a frame-less daemon recognize the Mac
display before its first GUI frame exists, and starts AppKit event
processing after that display is initialized. The author reports passing
the server and client tests and manually testing TTY and GUI clients in
both orders.</p>

<p>The patch is small and cleanly mergeable, but it has no human review
or project CI result yet. This is the first credible candidate I have
found for the whole Mac Port problem. This two-day-old code could be
just the piece we needed. Right now it is awaiting review.</p>

<p>The state of play as of September 1, 2026, is:</p>

<table>
  <thead>
    <tr>
      <th>Setup</th>
      <th>GUI and TTY frames together</th>
      <th>Frame-less daemon can create its first GUI frame</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>Standard GNU Emacs build</td>
      <td>Yes</td>
      <td>Yes</td>
    </tr>
    <tr>
      <td>Unpatched Emacs Mac Port</td>
      <td>No</td>
      <td>No</td>
    </tr>
    <tr>
      <td>Railwaycat-patched Mac Port</td>
      <td>Yes, if GUI-first</td>
      <td>No</td>
    </tr>
    <tr>
      <td>PR #143 applied to experimental Emacs Mac 30</td>
      <td>Author reports yes</td>
      <td>Author reports yes</td>
    </tr>
  </tbody>
</table>

<h2 id="so-multi-ttwhy">So, multi-ttwhy?</h2>

<p>The best way to solve a problem is sometimes to let it be the problem of
someone with more time or talent. Here, GNU Emacs already had the model,
Railwaycat carries the practical Mac Port patch, and now a new pull
request attempts the last daemon-to-GUI step.</p>

<p>The prospective full fix is only two days old, but our long international parenthetical editing nightmare may soon be over.</p>]]></content><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><summary type="html"><![CDATA[I want to have just one warm Emacs process. From it, I want to open a graphical frame when working in my OS GUI, open a text frame from a terminal or over SSH, and have both be views onto the same buffers and the same Lisp world. This does not seem like a big ask. Emacs has a server; emacsclient has flags for exactly this:]]></summary></entry><entry><title type="html">So many summer schools, not enough summer</title><link href="https://hemann.pl/so-many-summer-schools/" rel="alternate" type="text/html" title="So many summer schools, not enough summer" /><published>2026-09-01T00:00:00+00:00</published><updated>2026-09-01T00:00:00+00:00</updated><id>https://hemann.pl/so-many-summer-schools</id><content type="html" xml:base="https://hemann.pl/so-many-summer-schools/"><![CDATA[<p>I feel like there has never been a better time to find ancillary/enrichment PL school. For a while there were only a handful of these summer schools. And the ones I knew of were great. Looking at you OPLSS. But it just feels like over the last decade or so we’ve had a whole bunch more spring up. I got lucky this year and just happened to be going to FLoC for FoPSS. But there are jillions.</p>

<p>With a little googling, I pulled together a list. ‘Programming languages’ is flexible here; formal methods, verification, compilers, PL theory, types, and functional and logic programming and fellow travellers. My filter criteria was ‘how interested would I be to go to this and how much would I get out of it that relates to my research area’. I asked Claude to look back for historic dates when apps opened. This is mostly retrospective, but if past precedent has any relation to future stuff, then maybe this will also tell you when to start paying attention in 2027 and beyond.</p>

<p>“Summer school” is used loosely. The list includes spring and winter schools when they serve the same research-level purpose.</p>

<p>As I said, I also use “PL” broadly: beyond the aforementioned, stuff like synthesis, rewriting, analysis, logic, proof theory, category theory, and other areas belong when the connection runs through how programs are expressed, implemented, analyzed, or proved correct.</p>

<p>I can personally speak to PLMW, OPLSS, ICLP DC, SPLV, FoPSS, and NASSLLI. All were great. I know that I know colleagues whose word I would take as gospel who can recommend PLISS, MGS, and the Dutch one.</p>

<h2 id="calendar">Calendar</h2>

<p>These are month-sized reminders, not promises. Some school names link to a note below; the rest go to the official site.</p>

<h3 id="direct-schools">Direct schools</h3>

<table>
  <thead>
    <tr>
      <th>School</th>
      <th>Held</th>
      <th>Start watching</th>
      <th>Admission or reg.</th>
      <th>Funding, housing, or payment</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="#plmw">PLMW</a> (conf.-attached; normally 1 day)</td>
      <td>throughout the year</td>
      <td>preceding autumn, then Feb. and May</td>
      <td>conf. reg.</td>
      <td>funded participation: Nov. for POPL; April–June for others</td>
    </tr>
    <tr>
      <td><a href="https://www.cs.nott.ac.uk/MGS/">Midlands Graduate School (MGS)</a></td>
      <td>early April</td>
      <td>Jan.</td>
      <td>March</td>
      <td>—</td>
    </tr>
    <tr>
      <td><a href="https://www.rise-swu.cn/SETSS2026/">SETSS</a></td>
      <td>April–May</td>
      <td>Jan.–Feb.</td>
      <td>varies; no dependable cutoff</td>
      <td>—</td>
    </tr>
    <tr>
      <td><a href="https://pliss.org/">PLISS</a></td>
      <td>late May recently; sometimes Sept.</td>
      <td>early Dec. for a May school</td>
      <td>Jan.</td>
      <td>support is part of the same app</td>
    </tr>
    <tr>
      <td><a href="https://ssft-sri.github.io/">SSFT</a></td>
      <td>late May</td>
      <td>Dec.–Feb.</td>
      <td>March–April</td>
      <td>support may be tied to the app</td>
    </tr>
    <tr>
      <td><a href="#flolac">FLOLAC</a></td>
      <td>June–Aug.</td>
      <td>Jan. for special tracks; May for the course</td>
      <td>very brief June–July windows</td>
      <td>—</td>
    </tr>
    <tr>
      <td><a href="https://www.cs.uoregon.edu/research/summerschool/archives.html">OPLSS</a></td>
      <td>June–early July</td>
      <td>Jan.</td>
      <td>selective admission: March–April</td>
      <td>housing and payment: May</td>
    </tr>
    <tr>
      <td><a href="https://uu-afp.github.io/">Utrecht Advanced Functional Programming</a></td>
      <td>early July when held</td>
      <td>March</td>
      <td>June</td>
      <td>no 2026 edition so far</td>
    </tr>
    <tr>
      <td><a href="https://sat-smt-ar-school.gitlab.io/www/">SAT/SMT/AR Summer School</a> (conf.-attached)</td>
      <td>June–Aug.</td>
      <td>April</td>
      <td>May, or central conf. reg.</td>
      <td>—</td>
    </tr>
    <tr>
      <td><a href="https://2026.ecoop.org/track/ecoop-2026-academy">ECOOP Academy</a> (conf.-attached; multiday)</td>
      <td>late June–early July in 2026</td>
      <td>early spring</td>
      <td>conf. reg.</td>
      <td>conf. early rate; May in 2026</td>
    </tr>
    <tr>
      <td><a href="#iclp-dc-and-summer-school">ICLP Doctoral Consortium and Summer School</a> (conf.-attached; 1–2 days)</td>
      <td>July–Sept.</td>
      <td>early spring</td>
      <td>DC submissions: April–May</td>
      <td>conf. support varies</td>
    </tr>
    <tr>
      <td><a href="#splv">SPLV</a></td>
      <td>late July–early Aug.</td>
      <td>March</td>
      <td>ordinary reg.: June–July</td>
      <td>support and housing: April–May; early rate: ~June</td>
    </tr>
    <tr>
      <td><a href="https://sites.google.com/view/marktoberdorf2026/home">Marktoberdorf</a></td>
      <td>early–mid-Aug.</td>
      <td>early April</td>
      <td>selective app: early–mid-May</td>
      <td>payment: ~June</td>
    </tr>
    <tr>
      <td><a href="#vtsa">VTSA</a></td>
      <td>July–Sept.</td>
      <td>April–May</td>
      <td>selective app: May–July; accepted-applicant reg. later</td>
      <td>housing blocks: June–July</td>
    </tr>
    <tr>
      <td><a href="#international-school-on-rewriting">International School on Rewriting (ISR)</a></td>
      <td>July–Sept. recently</td>
      <td>Nov., then April</td>
      <td>early reg.: May–June</td>
      <td>travel support may close in May</td>
    </tr>
    <tr>
      <td><a href="https://www.proofsociety.org/">Proof Society School</a></td>
      <td>Sept. recently</td>
      <td>spring</td>
      <td>reg.: July</td>
      <td>grants: June–July</td>
    </tr>
    <tr>
      <td><a href="https://www.mathematik.uni-muenchen.de/~schwicht/pc25.php">Proof and Computation</a></td>
      <td>Sept.</td>
      <td>May</td>
      <td>selective app: late May–mid-June</td>
      <td>—</td>
    </tr>
    <tr>
      <td><a href="#ictac-postgraduate-school">ICTAC Postgraduate School</a> (conf.-attached; 2 days in 2026)</td>
      <td>early Nov. in 2026</td>
      <td>midyear</td>
      <td>conf. reg.</td>
      <td>—</td>
    </tr>
    <tr>
      <td><a href="https://sbmf2025.ufrpe.br/etmf.php">ETMF</a> (conf.-attached; recently 1 day)</td>
      <td>early Dec. recently</td>
      <td>Sept.–Oct.</td>
      <td>conf. reg.</td>
      <td>conf. early rate; Nov. in 2025</td>
    </tr>
    <tr>
      <td><a href="#fopss">FoPSS</a></td>
      <td>Feb., July, or Oct. recently; mobile in Europe</td>
      <td>Nov., then April</td>
      <td>edition-specific</td>
      <td>scholarships and early rates are edition-specific</td>
    </tr>
  </tbody>
</table>

<h3 id="related-or-special-topic-schools">Related or special-topic schools</h3>

<table>
  <thead>
    <tr>
      <th>School</th>
      <th>Held</th>
      <th>Start watching</th>
      <th>Admission or reg.</th>
      <th>Funding, housing, or payment</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://logicindia.org/isla/">Indian School on Logic and its Applications (ISLA)</a> (general logic)</td>
      <td>May–June</td>
      <td>Feb. of even years</td>
      <td>May</td>
      <td>no consistent separate cutoff</td>
    </tr>
    <tr>
      <td><a href="https://folli.info/?page_id=49">NASSLLI</a> (logic, language, and information)</td>
      <td>June</td>
      <td>Nov. for course proposals; Feb. for attendance</td>
      <td>attendee reg.: April–May</td>
      <td>grants: Feb.–March; early rate: ~April</td>
    </tr>
    <tr>
      <td><a href="https://www.hipeac.net/acaces/2026/">ACACES</a> (architecture and embedded compilation)</td>
      <td>mid-July</td>
      <td>late March</td>
      <td>apps: May; final reg.: June</td>
      <td>grants with the May app; payment in June</td>
    </tr>
    <tr>
      <td><a href="https://cybersecurity-research.be/summer-school-on-security-testing-and-verification-2026/">Security Testing &amp; Verification</a> (software security, testing, and verification)</td>
      <td>July or early Sept.</td>
      <td>May</td>
      <td>selective cutoff not reliably published</td>
      <td>early rate: July–Aug.</td>
    </tr>
    <tr>
      <td><a href="https://ufal.mff.cuni.cz/esslli-2026">ESSLLI</a> (logic, language, and information)</td>
      <td>late July–mid-Aug.</td>
      <td>Jan.–Feb.</td>
      <td>final reg.: July</td>
      <td>support: March–April; early rate: May–June</td>
    </tr>
    <tr>
      <td><a href="https://sites.google.com/uniurb.it/fosad">FOSAD</a> (security analysis and design)</td>
      <td>late Aug.</td>
      <td>June</td>
      <td>app: July</td>
      <td>scholarships and discounted fees are tied to the app</td>
    </tr>
    <tr>
      <td><a href="https://dare-milan.github.io/">DARE</a> (distributed systems; programming models, verification, testing, and debugging)</td>
      <td>late Aug.–Sept.</td>
      <td>mid-April</td>
      <td>apps: May–June</td>
      <td>no separate support cutoff listed</td>
    </tr>
    <tr>
      <td><a href="https://www.logic.at/tbilisi26/">International Tbilisi Summer School in Logic and Language</a> (broader logic and language)</td>
      <td>Sept.</td>
      <td>May–June</td>
      <td>varies</td>
      <td>waivers and co-location routes vary</td>
    </tr>
    <tr>
      <td><a href="https://comp.anu.edu.au/lss/">ANU Logic Summer School</a> (general logic)</td>
      <td>Dec.</td>
      <td>Sept.</td>
      <td>varies</td>
      <td>check grants in Oct.</td>
    </tr>
  </tbody>
</table>

<h3 id="programs-to-check-each-year">Programs to check each year</h3>

<p>Both recur, but their topics change; check the year’s program to see whether it belongs here.</p>

<table>
  <thead>
    <tr>
      <th>Program</th>
      <th>Held</th>
      <th>Start watching</th>
      <th>Admission or reg.</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="https://cs.ioc.ee/ewscs/">Estonian Winter School in Computer Science (EWSCS)</a></td>
      <td>early March</td>
      <td>early Jan.</td>
      <td>Feb.</td>
    </tr>
    <tr>
      <td><a href="https://india.acm.org/education/about-summer-schools">ACM India Summer and Winter Schools</a></td>
      <td>summer and winter programs</td>
      <td>Jan.–March for summer; Aug.–Oct. for winter</td>
      <td>edition-specific</td>
    </tr>
  </tbody>
</table>

<h3 id="emerging-schools">Emerging schools</h3>

<table>
  <thead>
    <tr>
      <th>School</th>
      <th>Held</th>
      <th>Start watching</th>
      <th>Admission or reg.</th>
      <th>Funding, housing, or payment</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><a href="#dutch-winter-school-on-logic-and-verification">Dutch Winter School on Logic and Verification</a></td>
      <td>Jan.</td>
      <td>Oct.</td>
      <td>Dec.–Jan.</td>
      <td>hotel block: ~Nov.</td>
    </tr>
    <tr>
      <td><a href="https://absint26.liparischool.it/">Lipari Summer School on Abstract Interpretation</a></td>
      <td>late Aug.–early Sept.</td>
      <td>March–April</td>
      <td>early rate: June; final reg.: July</td>
      <td>payment: July</td>
    </tr>
  </tbody>
</table>

<h2 id="plmw">PLMW</h2>

<p>The <a href="https://www.sigplan.org/Conferences/PLMW/">Programming Languages Mentoring Workshop</a> has editions at POPL, PLDI, ICFP, and SPLASH. It is normally a one-day workshop for senior undergraduates and early graduate students, combining introductory PL research talks with advice about graduate school and research careers. Ordinary attendance comes through conference registration; funded participation is a separate app. In 2026 those support deadlines fell in November for <a href="https://popl26.sigplan.org/home/PLMW-POPL-2026">POPL</a>, April for <a href="https://pldi26.sigplan.org/home/PLMW-PLDI-2026">PLDI</a>, and June for <a href="https://www.sigplan.org/announce/2026-05-29-plmw-icfp-2026/">ICFP</a>.</p>

<h2 id="flolac">FLOLAC</h2>

<p><a href="https://flolac.iis.sinica.edu.tw/en/2026/">FLOLAC</a> alternates emphasis: even years tend toward PL and type theory, while odd years tend toward automated verification and model checking. Its registration behaves partly like a National Taiwan University course rather than an ordinary international school, and the external-student window may last only 1–2 days. Set a January reminder for special tracks and a May reminder for the ordinary course timeline.</p>

<h2 id="iclp-dc-and-summer-school">ICLP DC and Summer School</h2>

<p>The <a href="https://sites.google.com/view/iclp-dc-ss2026/call-for-papers">ICLP DC and Summer School</a> is a conference-attached, submission-based opportunity for doctoral students in logic programming. Recent meetings have lasted 1–2 days. Its April–May dates are DC submission deadlines, not ordinary school registration deadlines.</p>

<h2 id="splv">SPLV</h2>

<p><a href="https://spli.scot/splv/">SPLV</a> rotates among Scottish universities. Scholarships, travel support, early registration, and subsidised housing may all close before ordinary registration. Apply in April for money or cheap housing; ordinary registration is a later process.</p>

<h2 id="vtsa">VTSA</h2>

<p>The <a href="https://conferences.mpi-inf.mpg.de/rg1/vtsa26/">Verification Technology, Systems and Applications school</a> is mobile, so “summer” is more reliable than a particular month. Its selective app, accepted-applicant registration, and housing blocks are separate processes. In particular, a later registration opening for accepted applicants is not the opening of selective apps.</p>

<h2 id="international-school-on-rewriting">International School on Rewriting</h2>

<p><a href="https://ifip-wg-rewriting.cs.ru.nl/summerschool.html">ISR</a> moves among hosts and months. Its subject matter—term and graph rewriting, theorem proving, semantics, and protocol verification—is squarely within this calendar. Announcements can appear in November; early registration and associated travel-support deadlines tend to follow in May or June.</p>

<h2 id="ictac-postgraduate-school">ICTAC Postgraduate School</h2>

<p>The <a href="https://ictac2026.github.io/">ICTAC Postgraduate School</a> is a short conference satellite; the 2026 school ran in early Nov. It offered three intensive courses on formal methods for mission-critical software and was aimed at PhD students and young researchers. Attendance used conference registration; no independent app was listed.</p>

<h2 id="fopss">FoPSS</h2>

<p><a href="https://etaps.org/about/fopss-schools/">FoPSS</a> is one mobile, 1-week European school per edition. The official history shows hosts in Portugal, the UK, Poland, Italy, and Denmark, with a different theme each time. Recent editions fell in Feb., July, and Oct.; check the central ETAPS page and <a href="https://lists.seas.upenn.edu/mailman/listinfo/types-announce">TYPES announcements</a> in Nov. and again in April.</p>

<h2 id="dutch-winter-school-on-logic-and-verification">Dutch Winter School on Logic and Verification</h2>

<p>The <a href="https://cyclic-structures.gitlab.io/school2026/">Dutch Winter School on Logic and Verification</a> covered concurrency semantics, coalgebra, Agda and dependent types, session types, and concurrent separation logic. Its January 2026 program suggests watching from October, with hotel and early-registration deadlines arriving before the school’s final registration cutoff.</p>

<p>Remember that the money, cheap housing, and selective-admission deadlines are often weeks or months earlier than ordinary registration.</p>]]></content><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><summary type="html"><![CDATA[I feel like there has never been a better time to find ancillary/enrichment PL school. For a while there were only a handful of these summer schools. And the ones I knew of were great. Looking at you OPLSS. But it just feels like over the last decade or so we’ve had a whole bunch more spring up. I got lucky this year and just happened to be going to FLoC for FoPSS. But there are jillions.]]></summary></entry><entry><title type="html">How to (try to) turn an MS into (a shot at) a PhD (if you must).</title><link href="https://hemann.pl/how-to-try-to-turn-an-ms-into-a-shot-at-a-phd-if-you-must/" rel="alternate" type="text/html" title="How to (try to) turn an MS into (a shot at) a PhD (if you must)." /><published>2026-08-20T00:00:00+00:00</published><updated>2026-08-20T00:00:00+00:00</updated><id>https://hemann.pl/how-to-try-to-turn-an-ms-into-a-shot-at-a-phd-if-you-must</id><content type="html" xml:base="https://hemann.pl/how-to-try-to-turn-an-ms-into-a-shot-at-a-phd-if-you-must/"><![CDATA[<p>Warning: this is advice I have not tried, and I would not expect it to
have worked for me. It is, however, my best guess about how to create
an unofficial path where one otherwise might not exist.</p>

<p>Shriram Krishnamurthi’s <a href="https://parentheticallyspeaking.org/articles/us-cs-phd-faq/">FAQ on getting a CS PhD in the
US</a> is the
best general advice I know. What follows is about the narrower problem
of trying to use a US MS program as preparation for a PhD.</p>

<p>That usually means sustained work on an uncertain problem, some
artifact that makes the work visible, and a detailed letter from a
researcher who watched you do it. A thesis, manuscript, system, or
careful reproducibility result may supply the artifact.</p>

<p>This advice is for people who have not already been actively engaged
in a research lab, including people whose undergraduate program did
not give them that opportunity.</p>

<h2 id="the-difficulty">The difficulty</h2>

<p>At least in the US, an MS is not a small PhD. Standalone MS programs
are course-based professional degrees.</p>

<p>Unless stated otherwise, MS programs are ‘terminal’ and not designed
to turn their students into PhD applicants. I would not expect the
admissions staff, or even most faculty, to know how an MS student can
make that happen. This is an uncommon path, and you may have to create
it for yourself.</p>

<p>If this is nonetheless your quest, then make this the minimum test of
any MS program you consider: can students do independent study and
write a thesis as part of the degree?</p>

<p>I would apply to MS programs almost as though you were applying to
work with particular PhD advisors, then try to create a shadow
apprenticeship before you arrive. Choose programs around particular
researchers, not simply departments or course offerings.</p>

<p>Read enough of their recent work to understand what their groups are
actually doing, look at their students and research software, and begin
writing to people before you apply. You may have better luck by writing
to graduate students than to professors. Graduate students are often
closer to unfinished work and may have a small subproblem on which help
would actually be useful.</p>

<p>Everything I just said, except the graduate-student part, is what you
would have been doing to find a PhD position. Because you’re doing
essentially that.</p>

<p>“Free labor” alone isn’t sufficiently attractive: the scarce resource
is time and supervision. You need to offer free labor whose expected
supervisory cost is low—reproducing a result, getting a research
system running, handling an open issue, running experiments, improving
tooling, etc. Be reliably useful enough that involving you becomes
easier than not involving you.</p>

<p>You may have to send a lot of mail. Make each message specific enough
to show that you have read the work, follow up once, and expect most
attempts to go nowhere. If someone gives you an opening, do a killer
job, and ask for more.</p>

<p>The message should be cheap to evaluate. In a few sentences: say what
work of theirs you read or tried, identify the particular thing you
could do, and ask whether that contribution would be useful. Attach
evidence only when it helps them judge the offer: a small repository,
reproduction note, bug report, or short technical write-up is better
than even the most enthusiastic fan letter.</p>

<p>The goal need not be a publication; it should be serious enough to
produce a thesis, manuscript, system, or other clear artifact, and to
let an advisor write in detail about you.</p>

<p>You want to be able to earn a letter from this person. You want to
demonstrate enough capability and talent that they would recommend
their personal researcher friend at another school invest 300,000 and a half a decade in you.</p>

<p>Remember that if the MS is only a year and you apply to PhD programs
during your first fall, you would have only a few months in the program
to build those relationships and results. Even if you’ve had a
6-9-month collaboration before starting the MS, it may still be worth
choosing a two-year program or allowing an extra application cycle
rather than rushing the very part of the master’s you need most.</p>

<p>The attempt is to manufacture the missing apprenticeship: find the
people doing work you care about, make it easy for one of them to try
working with you, learn fast, become genuinely useful, and turn that
usefulness into visible research judgment.</p>

<p>This advice asks you to behave like a beginning graduate student before
you have received the apprenticeship that normally teaches someone how
to be one. This is an unfairly high bar, and it is why I would not have
expected this strategy to work for me. Nonetheless, I think this is the
most plausible way to turn an MS into a PhD apprenticeship.</p>

<p>If you can choose a route that already provides research mentorship,
choose it. If an MS is the route you must take, coursework is the
official program. The research apprenticeship is the unofficial
program you are trying to build inside it.</p>

<h2 id="further-reading">Further reading</h2>

<ul>
  <li><a href="https://www.cs.cmu.edu/~harchol/gradschooltalk.pdf">Mor Harchol-Balter, “Applying to Ph.D. Programs in Computer
Science”</a></li>
  <li><a href="https://dynaroars.github.io/phd-cs-us/demystify.pdf">ThanhVu Nguyen, “Demystifying PhD Admissions in Computer
Science”</a></li>
  <li><a href="https://www.cs.jhu.edu/~jason/advice/how-to-work-with-a-professor.html">Jason Eisner, “How to Do Research With a
Professor”</a></li>
</ul>]]></content><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><summary type="html"><![CDATA[Warning: this is advice I have not tried, and I would not expect it to have worked for me. It is, however, my best guess about how to create an unofficial path where one otherwise might not exist.]]></summary></entry><entry><title type="html">Make macOS tel: links open in Google Voice</title><link href="https://hemann.pl/google-voice-tel-handler/" rel="alternate" type="text/html" title="Make macOS tel: links open in Google Voice" /><published>2026-08-03T00:00:00+00:00</published><updated>2026-08-03T00:00:00+00:00</updated><id>https://hemann.pl/google-voice-tel-handler</id><content type="html" xml:base="https://hemann.pl/google-voice-tel-handler/"><![CDATA[<p>All I wanted was to be able to click on a phone number and have it do
something useful. One bald yak later, here we are. A phone number link
is just a URL. It often looks something like <code class="language-plaintext highlighter-rouge">tel:+18005550111</code>. It
would be nice to click on that and have it do something useful. Click
in Google Maps, click in your terminal, whatever.</p>

<p>On macOS, clicking that link does not necessarily mean your browser
gets to decide what happens. The operating system gets the first vote.
And my macOS system is sure eager to send it to FaceTime. Which I
don’t use and is a dead-end on my system.</p>

<p>Here’s how it works. LaunchServices looks for the application
registered as the handler for the <code class="language-plaintext highlighter-rouge">tel:</code> URL scheme, and it sends the
URL there. Meaning if FaceTime is the current default handler, then
clicking a phone number in Google Maps, in Chrome, can still end up in
FaceTime. No amount of poking through Chrome preferences will fix it.
You can see if you try the more general:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>open <span class="s1">'tel:+18005550111'</span>
</code></pre></div></div>

<p>Boom. FaceTime. From macOS.</p>

<p>I wanted all the <code class="language-plaintext highlighter-rouge">tel:</code> links that the system knows how to dispatch to
open my Google Voice account instead. From the browser, from a
terminal, from a document, from whatever you can think of.</p>

<p>That’s the reason for the little helper app:</p>

<p><a href="https://github.com/jasonhemann/google-voice-tel-handler">google-voice-tel-handler</a></p>

<h2 id="what-the-helper-does">What the helper does</h2>

<p>This is an AppleScript applet with an <code class="language-plaintext highlighter-rouge">on open location</code> handler. The
OS can send URL events to an applet, so the script receives the
original <code class="language-plaintext highlighter-rouge">tel:</code> URL, extracts the phone number, and opens the
corresponding Google Voice call URL in the default browser.</p>

<p>The script accepts the common shapes I’ve seen:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tel:+18005550111
tel://+18005550111
tel:800-555-0111
</code></pre></div></div>

<p>It strips the <code class="language-plaintext highlighter-rouge">tel:</code> wrapper, keeps the digits, and opens a Google Voice URL
using the <code class="language-plaintext highlighter-rouge">/u/0</code> account slot:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>https://voice.google.com/u/0/calls?a=nc,%2B18005550111
</code></pre></div></div>

<p>Your default browser takes it from there.</p>

<h2 id="how-it-works">How it works</h2>

<p>You can see all the source code. It’s not much, and the AppleScript
itself is pretty readable.</p>

<p>There’s a build script that compiles the applet with
<code class="language-plaintext highlighter-rouge">osacompile</code>, then patches the generated <code class="language-plaintext highlighter-rouge">Info.plist</code> so the bundle has a
stable identity and declares the URL scheme:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>CFBundleIdentifier = com.jasonhemann.google-voice-tel-handler
CFBundleURLTypes   = tel
</code></pre></div></div>

<p>The tools involved are already on macOS: <code class="language-plaintext highlighter-rouge">osacompile</code>, <code class="language-plaintext highlighter-rouge">PlistBuddy</code>,
and <code class="language-plaintext highlighter-rouge">codesign</code>.</p>]]></content><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><summary type="html"><![CDATA[All I wanted was to be able to click on a phone number and have it do something useful. One bald yak later, here we are. A phone number link is just a URL. It often looks something like tel:+18005550111. It would be nice to click on that and have it do something useful. Click in Google Maps, click in your terminal, whatever.]]></summary></entry><entry><title type="html">Why some reverse functions are slow</title><link href="https://hemann.pl/why-some-reverse-functions-are-slow/" rel="alternate" type="text/html" title="Why some reverse functions are slow" /><published>2026-08-03T00:00:00+00:00</published><updated>2026-08-03T00:00:00+00:00</updated><id>https://hemann.pl/why-some-reverse-functions-are-slow</id><content type="html" xml:base="https://hemann.pl/why-some-reverse-functions-are-slow/"><![CDATA[<p>I overheard two students discussing “the time complexity of reverse,” without
any more concrete referent. This post is a response for those students.</p>

<p>If you ask whether <code class="language-plaintext highlighter-rouge">reverse</code> is fast, the right first response is: which
<code class="language-plaintext highlighter-rouge">reverse</code>?</p>

<p>There is a mathematical function called reverse. It takes a list like
<code class="language-plaintext highlighter-rouge">'(1 2 3)</code> and produces <code class="language-plaintext highlighter-rouge">'(3 2 1)</code>. But there are many algorithms that compute
that function. Some are linear time. Some are quadratic time. The result is the
same; the amount of work can be very different.</p>

<p>Here are two Racket programs that both reverse a list:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">rev1</span> <span class="nv">l</span><span class="p">)</span>
  <span class="p">(</span><span class="k">cond</span>
    <span class="p">[(</span><span class="nf">empty?</span> <span class="nv">l</span><span class="p">)</span> <span class="o">'</span><span class="p">()]</span>
    <span class="p">[(</span><span class="nf">cons?</span> <span class="nv">l</span><span class="p">)</span>
     <span class="p">(</span><span class="nb">append</span> <span class="p">(</span><span class="nf">rev1</span> <span class="p">(</span><span class="nf">rest</span> <span class="nv">l</span><span class="p">))</span>
             <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">first</span> <span class="nv">l</span><span class="p">)))]))</span>

<span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">rev2</span> <span class="nv">l</span><span class="p">)</span>
  <span class="p">(</span><span class="nf">rev2-help</span> <span class="nv">l</span> <span class="o">'</span><span class="p">()))</span>

<span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">rev2-help</span> <span class="nv">l</span> <span class="nv">acc</span><span class="p">)</span>
  <span class="p">(</span><span class="k">cond</span>
    <span class="p">[(</span><span class="nf">empty?</span> <span class="nv">l</span><span class="p">)</span> <span class="nv">acc</span><span class="p">]</span>
    <span class="p">[(</span><span class="nf">cons?</span> <span class="nv">l</span><span class="p">)</span>
     <span class="p">(</span><span class="nf">rev2-help</span> <span class="p">(</span><span class="nf">rest</span> <span class="nv">l</span><span class="p">)</span>
                <span class="p">(</span><span class="nb">cons</span> <span class="p">(</span><span class="nf">first</span> <span class="nv">l</span><span class="p">)</span> <span class="nv">acc</span><span class="p">))]))</span>
</code></pre></div></div>

<p>They compute the same answers:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">&gt;</span> <span class="p">(</span><span class="nf">rev1</span> <span class="o">'</span><span class="p">(</span><span class="nf">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">))</span>
<span class="o">'</span><span class="p">(</span><span class="nf">4</span> <span class="mi">3</span> <span class="mi">2</span> <span class="mi">1</span><span class="p">)</span>

<span class="nv">&gt;</span> <span class="p">(</span><span class="nf">rev2</span> <span class="o">'</span><span class="p">(</span><span class="nf">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">))</span>
<span class="o">'</span><span class="p">(</span><span class="nf">4</span> <span class="mi">3</span> <span class="mi">2</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<p>But <code class="language-plaintext highlighter-rouge">rev1</code> is slower for large lists. The reason is not mysterious once we look
at what each step is forced to do.</p>

<p>Throughout, the complexity measure is time as a function of the length of the
input list. When we say <code class="language-plaintext highlighter-rouge">O(n)</code> or <code class="language-plaintext highlighter-rouge">O(n^2)</code>, <code class="language-plaintext highlighter-rouge">n</code> is the number of elements in the
list being reversed.</p>

<h2 id="lists-are-one-way">Lists Are One-Way</h2>

<p>A Racket list is built out of pairs. Each pair points at the next part of the
list. That makes some operations cheap:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">first</span> <span class="o">'</span><span class="p">(</span><span class="nf">1</span> <span class="mi">2</span> <span class="mi">3</span><span class="p">))</span> <span class="c1">; 1</span>
<span class="p">(</span><span class="nf">rest</span>  <span class="o">'</span><span class="p">(</span><span class="nf">1</span> <span class="mi">2</span> <span class="mi">3</span><span class="p">))</span> <span class="c1">; '(2 3)</span>
<span class="p">(</span><span class="nb">cons</span> <span class="mi">0</span> <span class="o">'</span><span class="p">(</span><span class="nf">1</span> <span class="mi">2</span> <span class="mi">3</span><span class="p">))</span> <span class="c1">; '(0 1 2 3)</span>
</code></pre></div></div>

<p>Each of those operations can be treated as constant time for this discussion.
They look at, or add, the front of the list.</p>

<p>But <code class="language-plaintext highlighter-rouge">append</code> is different. To append one list onto another, Racket has to walk
down the first list. For example:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nb">append</span> <span class="o">'</span><span class="p">(</span><span class="nf">1</span> <span class="mi">2</span> <span class="mi">3</span><span class="p">)</span> <span class="o">'</span><span class="p">(</span><span class="nf">4</span> <span class="mi">5</span><span class="p">))</span>
</code></pre></div></div>

<p>The result is <code class="language-plaintext highlighter-rouge">'(1 2 3 4 5)</code>, but getting there requires visiting the elements
of <code class="language-plaintext highlighter-rouge">'(1 2 3)</code>. If the first list has length <code class="language-plaintext highlighter-rouge">m</code>, the append takes time
proportional to <code class="language-plaintext highlighter-rouge">m</code>.</p>

<p>That one fact explains the difference between the two versions of <code class="language-plaintext highlighter-rouge">reverse</code>.</p>

<h2 id="the-direct-version">The Direct Version</h2>

<p>The direct version says:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">rev1</span> <span class="nv">l</span><span class="p">)</span>
  <span class="p">(</span><span class="k">cond</span>
    <span class="p">[(</span><span class="nf">empty?</span> <span class="nv">l</span><span class="p">)</span> <span class="o">'</span><span class="p">()]</span>
    <span class="p">[(</span><span class="nf">cons?</span> <span class="nv">l</span><span class="p">)</span>
     <span class="p">(</span><span class="nb">append</span> <span class="p">(</span><span class="nf">rev1</span> <span class="p">(</span><span class="nf">rest</span> <span class="nv">l</span><span class="p">))</span>
             <span class="p">(</span><span class="nb">list</span> <span class="p">(</span><span class="nf">first</span> <span class="nv">l</span><span class="p">)))]))</span>
</code></pre></div></div>

<p>In English:</p>

<ol>
  <li>Reverse the rest of the list.</li>
  <li>Put the first element at the end.</li>
</ol>

<p>For a small list, that is perfectly clear:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">rev1</span> <span class="o">'</span><span class="p">(</span><span class="nf">1</span> <span class="mi">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">))</span>
<span class="nv">=</span> <span class="p">(</span><span class="nb">append</span> <span class="p">(</span><span class="nf">rev1</span> <span class="o">'</span><span class="p">(</span><span class="nf">2</span> <span class="mi">3</span> <span class="mi">4</span><span class="p">))</span> <span class="o">'</span><span class="p">(</span><span class="nf">1</span><span class="p">))</span>
<span class="nv">=</span> <span class="p">(</span><span class="nb">append</span> <span class="p">(</span><span class="nb">append</span> <span class="p">(</span><span class="nf">rev1</span> <span class="o">'</span><span class="p">(</span><span class="nf">3</span> <span class="mi">4</span><span class="p">))</span> <span class="o">'</span><span class="p">(</span><span class="nf">2</span><span class="p">))</span> <span class="o">'</span><span class="p">(</span><span class="nf">1</span><span class="p">))</span>
<span class="nv">=</span> <span class="p">(</span><span class="nb">append</span> <span class="p">(</span><span class="nb">append</span> <span class="p">(</span><span class="nb">append</span> <span class="p">(</span><span class="nf">rev1</span> <span class="o">'</span><span class="p">(</span><span class="nf">4</span><span class="p">))</span> <span class="o">'</span><span class="p">(</span><span class="nf">3</span><span class="p">))</span> <span class="o">'</span><span class="p">(</span><span class="nf">2</span><span class="p">))</span> <span class="o">'</span><span class="p">(</span><span class="nf">1</span><span class="p">))</span>
<span class="nv">=</span> <span class="o">'</span><span class="p">(</span><span class="nf">4</span> <span class="mi">3</span> <span class="mi">2</span> <span class="mi">1</span><span class="p">)</span>
</code></pre></div></div>

<p>The expensive part is hidden in “put the first element at the end.” Since lists
are easy to add to at the front, but not at the back, <code class="language-plaintext highlighter-rouge">rev1</code> uses <code class="language-plaintext highlighter-rouge">append</code>.</p>

<p>For a list of length 4, the append work has this shape:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>append a list of length 0
append a list of length 1
append a list of length 2
append a list of length 3
</code></pre></div></div>

<p>For a list of length <code class="language-plaintext highlighter-rouge">n</code>, the append work has this shape:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 + 1 + 2 + ... + (n - 1)
</code></pre></div></div>

<p>That sum is the familiar triangular number:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 + 1 + 2 + ... + (n - 1) = n(n - 1) / 2
</code></pre></div></div>

<p>The important part is the <code class="language-plaintext highlighter-rouge">n^2</code>. The exact constants do not matter for big-O
analysis, so this version is <code class="language-plaintext highlighter-rouge">O(n^2)</code>.</p>

<p>We can also say this with a recurrence. Let <code class="language-plaintext highlighter-rouge">R(n)</code> be the cost of reversing a
list of length <code class="language-plaintext highlighter-rouge">n</code> with <code class="language-plaintext highlighter-rouge">rev1</code>. Let <code class="language-plaintext highlighter-rouge">A(m)</code> be the cost of appending a first list
of length <code class="language-plaintext highlighter-rouge">m</code>. These costs are still measured in terms of list length: <code class="language-plaintext highlighter-rouge">R</code>
varies with the length of the list being reversed, and <code class="language-plaintext highlighter-rouge">A</code> varies with the
length of the first argument to <code class="language-plaintext highlighter-rouge">append</code>.</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>R(0) = constant
R(n) = R(n - 1) + A(n - 1) + constant
</code></pre></div></div>

<p>Since <code class="language-plaintext highlighter-rouge">A(n - 1)</code> is linear in <code class="language-plaintext highlighter-rouge">n - 1</code>, <code class="language-plaintext highlighter-rouge">R(n)</code> accumulates a linear amount of
append work at each level of recursion. Adding those linear pieces together
gives the quadratic sum above.</p>

<h2 id="the-accumulator-version">The Accumulator Version</h2>

<p>The second version uses a helper function:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">rev2</span> <span class="nv">l</span><span class="p">)</span>
  <span class="p">(</span><span class="nf">rev2-help</span> <span class="nv">l</span> <span class="o">'</span><span class="p">()))</span>

<span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">rev2-help</span> <span class="nv">l</span> <span class="nv">acc</span><span class="p">)</span>
  <span class="p">(</span><span class="k">cond</span>
    <span class="p">[(</span><span class="nf">empty?</span> <span class="nv">l</span><span class="p">)</span> <span class="nv">acc</span><span class="p">]</span>
    <span class="p">[(</span><span class="nf">cons?</span> <span class="nv">l</span><span class="p">)</span>
     <span class="p">(</span><span class="nf">rev2-help</span> <span class="p">(</span><span class="nf">rest</span> <span class="nv">l</span><span class="p">)</span>
                <span class="p">(</span><span class="nb">cons</span> <span class="p">(</span><span class="nf">first</span> <span class="nv">l</span><span class="p">)</span> <span class="nv">acc</span><span class="p">))]))</span>
</code></pre></div></div>

<p>This program carries an accumulator, <code class="language-plaintext highlighter-rouge">acc</code>, that represents the part of the
answer built so far. At each step it removes one element from the front of the
input list and adds that element to the front of the accumulator.</p>

<p>Here is the state of the computation for <code class="language-plaintext highlighter-rouge">'(1 2 3 4)</code>:</p>

<table>
  <thead>
    <tr>
      <th>Remaining input</th>
      <th>Accumulator</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">'(1 2 3 4)</code></td>
      <td><code class="language-plaintext highlighter-rouge">'()</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">'(2 3 4)</code></td>
      <td><code class="language-plaintext highlighter-rouge">'(1)</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">'(3 4)</code></td>
      <td><code class="language-plaintext highlighter-rouge">'(2 1)</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">'(4)</code></td>
      <td><code class="language-plaintext highlighter-rouge">'(3 2 1)</code></td>
    </tr>
    <tr>
      <td><code class="language-plaintext highlighter-rouge">'()</code></td>
      <td><code class="language-plaintext highlighter-rouge">'(4 3 2 1)</code></td>
    </tr>
  </tbody>
</table>

<p>When the input is empty, the accumulator is the reversed list.</p>

<p>The key operation is <code class="language-plaintext highlighter-rouge">cons</code>, not <code class="language-plaintext highlighter-rouge">append</code>. Adding one item to the front of a
list is constant time. That means each recursive step does a constant amount of
work, then moves to a list that is one element shorter. The accumulator grows,
but the cost of <code class="language-plaintext highlighter-rouge">cons</code> does not grow with it.</p>

<p>If <code class="language-plaintext highlighter-rouge">H(n)</code> is the cost of <code class="language-plaintext highlighter-rouge">rev2-help</code> on a list of length <code class="language-plaintext highlighter-rouge">n</code>, then:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>H(0) = constant
H(n) = H(n - 1) + constant
</code></pre></div></div>

<p>After <code class="language-plaintext highlighter-rouge">n</code> steps, that is just:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>H(n) = constant * n + constant
</code></pre></div></div>

<p>So <code class="language-plaintext highlighter-rouge">rev2-help</code> is <code class="language-plaintext highlighter-rouge">O(n)</code>, and <code class="language-plaintext highlighter-rouge">rev2</code> is also <code class="language-plaintext highlighter-rouge">O(n)</code>.
Again, this is linear in the length of the original input list.</p>

<h2 id="the-lesson">The Lesson</h2>

<p>It is imprecise to say “the complexity of <code class="language-plaintext highlighter-rouge">reverse</code>” without saying which
algorithm we mean. The function being computed is not the whole story.</p>

<p><code class="language-plaintext highlighter-rouge">rev1</code> and <code class="language-plaintext highlighter-rouge">rev2</code> produce the same lists, but <code class="language-plaintext highlighter-rouge">rev1</code> repeatedly pays for
<code class="language-plaintext highlighter-rouge">append</code>, and those costs add up:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0 + 1 + 2 + ... + (n - 1)
</code></pre></div></div>

<p>That is why <code class="language-plaintext highlighter-rouge">rev1</code> is quadratic.</p>

<p><code class="language-plaintext highlighter-rouge">rev2</code> does one constant-time <code class="language-plaintext highlighter-rouge">cons</code> per input element. That is why <code class="language-plaintext highlighter-rouge">rev2</code> is
linear in the length of the list being reversed.</p>

<p>The built-in <code class="language-plaintext highlighter-rouge">reverse</code> you expect from a language implementation should be the
linear-time kind. But if you write your own, the difference between “put it on
the end” and “carry an accumulator” is the difference between an algorithm that
slows down quadratically and one that scales directly with the size of the list.</p>]]></content><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><summary type="html"><![CDATA[I overheard two students discussing “the time complexity of reverse,” without any more concrete referent. This post is a response for those students.]]></summary></entry><entry><title type="html">How does an interpreter work?</title><link href="https://hemann.pl/how-does-an-interpreter-work/" rel="alternate" type="text/html" title="How does an interpreter work?" /><published>2026-07-08T00:00:00+00:00</published><updated>2026-07-08T00:00:00+00:00</updated><id>https://hemann.pl/how-does-an-interpreter-work</id><content type="html" xml:base="https://hemann.pl/how-does-an-interpreter-work/"><![CDATA[<p>It sometimes helps to step through a non-trivial example of evaluation in order to
understand how an environment dynamically develops during the evaluation. We will
take this program as our running example:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">i</span><span class="p">)</span>
   <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
      <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
         <span class="nv">i</span><span class="p">)</span>
       <span class="mi">5</span><span class="p">))</span>
    <span class="mi">6</span><span class="p">))</span>
 <span class="mi">7</span><span class="p">)</span>
</code></pre></div></div>

<p>We will step through the execution of this program, but first let us acquaint
ourselves with what the program actually does.</p>

<h2 id="the-outer-application">The Outer Application</h2>

<p>At the top level, this is an application. It is the application of the operator
expression</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">i</span><span class="p">)</span>
  <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
     <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span> <span class="nv">i</span><span class="p">)</span> <span class="mi">5</span><span class="p">))</span>
   <span class="mi">6</span><span class="p">))</span>
</code></pre></div></div>

<p>to the operand <code class="language-plaintext highlighter-rouge">7</code>. In an application form, we call the first expression the
<strong><a href="https://www.youtube.com/watch?v=3RA4MykPm4s">operator</a></strong> and the second
expression the operand. The operator is a lambda expression. If I were unsure of
that, we could write a program over our lambda-calculus datatype:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">define</span> <span class="p">(</span><span class="nf">primary-form</span> <span class="nv">expr</span><span class="p">)</span>
  <span class="p">(</span><span class="nf">match</span> <span class="nv">expr</span>
    <span class="p">[</span><span class="o">`,</span><span class="nv">y</span> <span class="nt">#:when</span> <span class="p">(</span><span class="nb">symbol?</span> <span class="nv">y</span><span class="p">)</span> <span class="ss">'variable</span><span class="p">]</span>
    <span class="p">[</span><span class="o">`</span><span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="o">,</span><span class="nv">x</span><span class="p">)</span> <span class="o">,</span><span class="nv">body</span><span class="p">)</span> <span class="ss">'lambda</span><span class="p">]</span>
    <span class="p">[</span><span class="o">`</span><span class="p">(</span><span class="o">,</span><span class="nv">rator</span> <span class="o">,</span><span class="nv">rand</span><span class="p">)</span> <span class="ss">'application</span><span class="p">]))</span>
</code></pre></div></div>

<p>You can use this to verify the program for yourself. The body of that lambda
expression is also an application:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
   <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
      <span class="nv">i</span><span class="p">)</span> <span class="c1">; this will be the point we discuss most</span>
    <span class="mi">5</span><span class="p">))</span>
 <span class="mi">6</span><span class="p">)</span>
</code></pre></div></div>

<p>There are three useful things to notice. First, this is again an application of
a lambda whose body is also an application. Second, the ultimate value of this
whole big expression is going to come from the value of <code class="language-plaintext highlighter-rouge">i</code>. Third, <code class="language-plaintext highlighter-rouge">i</code> is a
free variable reference in</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
   <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span> <span class="nv">i</span><span class="p">)</span> <span class="mi">5</span><span class="p">))</span>
 <span class="mi">6</span><span class="p">)</span>
</code></pre></div></div>

<p>This latter fact should not be surprising. If <code class="language-plaintext highlighter-rouge">x</code> occurs in the scope of an
expression, such as in <code class="language-plaintext highlighter-rouge">(lambda (x) (lambda (y) x))</code>, then that is a bound
reference to <code class="language-plaintext highlighter-rouge">x</code>, since the variable declaration of <code class="language-plaintext highlighter-rouge">x</code> in the lambda expression
is the referent of that bound variable reference.</p>

<p>If we restrict our focus to the body of that same expression, <code class="language-plaintext highlighter-rouge">(lambda (y) x)</code>,
then there must be a free variable reference to <code class="language-plaintext highlighter-rouge">x</code> in the body. That outer
<code class="language-plaintext highlighter-rouge">(lambda (x) ...)</code> is what will bind that free variable.</p>

<h2 id="environments-remember-context">Environments Remember Context</h2>

<p>We perform evaluation of our programs via structural recursion, the same way we
write most other functions. That means the place where we actually want to get
the value of a variable is when we work our way down to the variable line. For
the program above, that happens when we have worked our way all the way down to
<code class="language-plaintext highlighter-rouge">i</code>.</p>

<p>By then, it is too late to figure out the value of <code class="language-plaintext highlighter-rouge">i</code> all by itself, because we
no longer have the information we need to determine what <code class="language-plaintext highlighter-rouge">i</code> means. Our solution
is to accumulate that information as we recur into the expression, and then look
up the meaning of bound variables in our accumulator when we reach a variable
case.</p>

<p>Since there are potentially scads and scads of bound variables in an expression,
we need to track each one differently. When we see <code class="language-plaintext highlighter-rouge">i</code>, we need to know to look
up <code class="language-plaintext highlighter-rouge">i</code> and not <code class="language-plaintext highlighter-rouge">x</code>. We also need to track not just the name of the variable, but
also its meaning. Our accumulator must therefore track two pieces for each
variable: the name, or formal parameter, of the lambda expression, and the value,
or actual argument, to which that formal parameter corresponds in this context.</p>

<p>When do we finally have both of those pieces? Look at the application line:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="nf">match</span> <span class="nv">expr</span>
  <span class="o">...</span>
  <span class="p">[</span><span class="o">`</span><span class="p">(</span><span class="o">,</span><span class="nv">rator</span> <span class="o">,</span><span class="nv">rand</span><span class="p">)</span> <span class="p">((</span><span class="nf">valof</span> <span class="nv">rator</span> <span class="nv">env</span><span class="p">)</span> <span class="p">(</span><span class="nf">valof</span> <span class="nv">rand</span> <span class="nv">env</span><span class="p">))])</span>
</code></pre></div></div>

<p>The <code class="language-plaintext highlighter-rouge">rator</code> has to evaluate to a Racket procedure, since we are about to call it
like a procedure of one argument. We are going to call that procedure on the
value of the <code class="language-plaintext highlighter-rouge">rand</code>. The <code class="language-plaintext highlighter-rouge">rator</code> and <code class="language-plaintext highlighter-rouge">rand</code> can both be big honkin’
expressions, but ultimately, when we get values for them and pass the results
back up the recursion, the value of the <code class="language-plaintext highlighter-rouge">rator</code> had better be a procedure. The
value of the <code class="language-plaintext highlighter-rouge">rand</code> can be any old value.</p>

<p>Only after we finish evaluating the two sub-pieces of the <code class="language-plaintext highlighter-rouge">rator</code>/<code class="language-plaintext highlighter-rouge">rand</code> form,
and then do the Racket application, do we have both the formal and actual
parameters at the same time.</p>

<h2 id="lambda-bodies-wait">Lambda Bodies Wait</h2>

<p>Here is a simpler example of something like our interpreter:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">b</span><span class="p">)</span> <span class="p">(</span><span class="k">if</span> <span class="nv">b</span> <span class="mi">5</span> <span class="mi">6</span><span class="p">))</span> <span class="p">(</span><span class="nb">not</span> <span class="nv">false</span><span class="p">))</span>
</code></pre></div></div>

<p>We first evaluate the operator and operand to values: the former to a Racket
procedure and the latter to a Racket boolean. Then we call the Racket procedure
on the Racket boolean. Only after that can we evaluate the body <code class="language-plaintext highlighter-rouge">(if b 5 6)</code>.</p>

<p>That is an important point: we never evaluate the body of a lambda expression
until after we have applied it to a value.</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">define</span> <span class="nv">loop</span>
  <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">x</span><span class="p">)</span>
    <span class="p">(</span><span class="nf">loop</span> <span class="nv">x</span><span class="p">)))</span>
</code></pre></div></div>

<p>The reason you can get by with writing down a definition like <code class="language-plaintext highlighter-rouge">loop</code> is that we
do not evaluate the body of the expression unless and until we invoke it with an
argument.</p>

<h2 id="extending-the-environment">Extending The Environment</h2>

<p>It is on the lambda line that we actually extend our environment. The right-hand
side of the lambda line is:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">a</span><span class="p">)</span>
  <span class="p">(</span><span class="nf">valof</span> <span class="nv">body</span> <span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y</span><span class="p">)</span>
                <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">eqv?</span> <span class="nv">x</span> <span class="nv">y</span><span class="p">)</span>
                    <span class="nv">a</span>
                    <span class="p">(</span><span class="nf">env</span> <span class="nv">y</span><span class="p">)))))</span>
</code></pre></div></div>

<p>That is the function we invoke with <code class="language-plaintext highlighter-rouge">true</code> or <code class="language-plaintext highlighter-rouge">7</code> or whatever value we have in
our example. It is not until we invoke that function that we begin to evaluate
the body. As soon as we recur in to evaluate the body, we will no longer have
access to the surrounding <code class="language-plaintext highlighter-rouge">(lambda (x) ...)</code>, because that was the point of
recurring down into the smaller expression.</p>

<p>To keep that information with us as we go, we add it to our accumulator, called
an environment. We are using a functional accumulator; we can just as well look
at our interpreter as a mathematical description of a particular function from
variables to values.</p>

<p>This step, adding to the accumulator, is also called <strong>extending</strong> the function.
If you think of a function as a set of pairs, then we have added one more pair:
the mapping of <code class="language-plaintext highlighter-rouge">(x, a)</code>. Notice also that if there were already a mapping from
<code class="language-plaintext highlighter-rouge">x</code>, we will have locally overwritten it. We call this behavior shadowing, and
the function extension in the lambda line shows how we implement that shadowing.</p>

<h2 id="walking-the-example">Walking The Example</h2>

<p>Now let us evaluate the big expression:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">i</span><span class="p">)</span>
   <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
      <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
         <span class="nv">i</span><span class="p">)</span>
       <span class="mi">5</span><span class="p">))</span>
    <span class="mi">6</span><span class="p">))</span>
 <span class="mi">7</span><span class="p">)</span>
</code></pre></div></div>

<p>We evaluate the two sub-pieces. The <code class="language-plaintext highlighter-rouge">rator</code> evaluates to a procedure, and the
<code class="language-plaintext highlighter-rouge">rand</code> evaluates to <code class="language-plaintext highlighter-rouge">7</code>. When we do the invocation, we start to evaluate the
body:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
   <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
      <span class="nv">i</span><span class="p">)</span>
    <span class="mi">5</span><span class="p">))</span>
 <span class="mi">6</span><span class="p">)</span>
</code></pre></div></div>

<p>We do so while keeping track of the association that <code class="language-plaintext highlighter-rouge">i</code> should mean <code class="language-plaintext highlighter-rouge">7</code>. If we
wrote the environment out by hand, it would now look like this:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y</span><span class="p">)</span>
  <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">eqv?</span> <span class="nv">y</span> <span class="ss">'i</span><span class="p">)</span>
      <span class="mi">7</span>
      <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y</span><span class="p">)</span> <span class="nv">&lt;bomb&gt;</span><span class="p">)</span>
       <span class="nv">y</span><span class="p">)))</span>
</code></pre></div></div>

<p>So we evaluate that body in this environment. In the same fashion, we evaluate
the two sub-pieces, produce the procedure and the value <code class="language-plaintext highlighter-rouge">6</code>, and then do the
application. That means we evaluate this body:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">j</span><span class="p">)</span>
   <span class="nv">i</span><span class="p">)</span>
 <span class="mi">5</span><span class="p">)</span>
</code></pre></div></div>

<p>with one additional binding, <code class="language-plaintext highlighter-rouge">j</code> to <code class="language-plaintext highlighter-rouge">6</code>. If we wrote the environment out by
hand, it would now look like this:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y</span><span class="p">)</span>
  <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">eqv?</span> <span class="nv">y</span> <span class="ss">'j</span><span class="p">)</span>
      <span class="mi">6</span>
      <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y</span><span class="p">)</span>
         <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">eqv?</span> <span class="nv">y</span> <span class="ss">'i</span><span class="p">)</span>
             <span class="mi">7</span>
             <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y</span><span class="p">)</span> <span class="nv">&lt;bomb&gt;</span><span class="p">)</span>
              <span class="nv">y</span><span class="p">)))</span>
       <span class="nv">y</span><span class="p">)))</span>
</code></pre></div></div>

<p>Each time we extend the environment, we wrap the old one with an <code class="language-plaintext highlighter-rouge">if</code> statement
that first checks for the variable we just added, and otherwise recurs to the
slightly smaller environment.</p>

<p>Doing the same again, we evaluate the two sub-pieces, do the Racket application,
and evaluate the body within the extended environment. In this case, that means
we evaluate <code class="language-plaintext highlighter-rouge">i</code> in the following environment:</p>

<div class="language-racket highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y</span><span class="p">)</span>
  <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">eqv?</span> <span class="nv">y</span> <span class="ss">'j</span><span class="p">)</span>
      <span class="mi">5</span>
      <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y</span><span class="p">)</span>
         <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">eqv?</span> <span class="nv">y</span> <span class="ss">'j</span><span class="p">)</span>
             <span class="mi">6</span>
             <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y</span><span class="p">)</span>
                <span class="p">(</span><span class="k">if</span> <span class="p">(</span><span class="nb">eqv?</span> <span class="nv">y</span> <span class="ss">'i</span><span class="p">)</span>
                    <span class="mi">7</span>
                    <span class="p">((</span><span class="k">lambda</span> <span class="p">(</span><span class="nf">y</span><span class="p">)</span> <span class="nv">&lt;bomb&gt;</span><span class="p">)</span>
                     <span class="nv">y</span><span class="p">)))</span>
              <span class="nv">y</span><span class="p">)))</span>
       <span class="nv">y</span><span class="p">)))</span>
</code></pre></div></div>

<p>Notice two things here. First, as we focus more tightly on a sub-expression of a
sub-expression of a sub-expression, our environment has grown larger and larger.
There is an inverse relationship between how tightly focused we are on a
sub-expression and how large a nested function our environment has become.</p>

<p>Second, the innermost binding for <code class="language-plaintext highlighter-rouge">j</code> in our original expression, <code class="language-plaintext highlighter-rouge">5</code>, has now
blocked out the ability to look up the other meaning of <code class="language-plaintext highlighter-rouge">j</code>. This is our
implementation of shadowing. Because we accumulate these bindings with the most
recent one in front, we can use the recursion into the program’s structure to
get the shadowing behavior right.</p>

<p>When we finally look up <code class="language-plaintext highlighter-rouge">i</code>, we check that it is not <code class="language-plaintext highlighter-rouge">j</code>, then that it is not
<code class="language-plaintext highlighter-rouge">j</code> again, and then finally that <code class="language-plaintext highlighter-rouge">'i</code> is <code class="language-plaintext highlighter-rouge">'i</code>. Since it is, the lookup returns
<code class="language-plaintext highlighter-rouge">7</code>, and the whole expression evaluates to <code class="language-plaintext highlighter-rouge">7</code>.</p>]]></content><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><summary type="html"><![CDATA[It sometimes helps to step through a non-trivial example of evaluation in order to understand how an environment dynamically develops during the evaluation. We will take this program as our running example:]]></summary></entry><entry><title type="html">Letter of recommendation (from a professor whose class you took)</title><link href="https://hemann.pl/dwic/" rel="alternate" type="text/html" title="Letter of recommendation (from a professor whose class you took)" /><published>2023-08-11T00:00:00+00:00</published><updated>2023-08-11T00:00:00+00:00</updated><id>https://hemann.pl/dwic</id><content type="html" xml:base="https://hemann.pl/dwic/"><![CDATA[<p>Let’s say you did well in an upper-division class with a professor. You enjoyed the class and also enjoyed learning the material. That might seem like it would make the professor a shoo-in as a good recommender for you for your graduate school application. Here’s why that might not be so! It’s important that someone gives you the full scoop on all this. For instance, if I wrote a student such a letter, I might even be doing them a disservice by taking the slot of someone whose word would go farther.</p>

<p>If you have heard the term “DWIC” before, then you’ll know what I’m about to say. Either way, please do make sure you see <a href="https://koronkevi.ch/posts/applying-to-grad-school.html#:~:text=A%20%22Did%20Well%20In%20Class,ability%20in%20classes%2C%20not%20research">Paulette’s thoughts</a>.The point is that “did well in my class” letters don’t count for much because everyone who submits an application did well in the class that they would have had with their letter writer. Anyone should be able to muster that kind of a letter, and so it doesn’t help distinguish the so-so candidates from the great ones. It’s basically table stakes.</p>

<p>Graduate schools typically want comprehensive letters of recommendation that give detailed, nuanced evaluations of your strengths and potential as a student. A letter that simply states that you did well in class doesn’t give much insight into your intellectual ability, research skills, leadership qualities, or any other relevant attributes that graduate schools are looking for in applicants. Good letters generally come from people who’ve seen you work on more long term projects, in either a small-group reading class, or an independent study or a research project. Sometimes this could be a co-op/internship supervisor, depending upon where you did your co-op/internship. A one-off professor can’t offer that kind of perspective, because they didn’t get a chance to work with you in that manner.</p>

<p>If you already have all that other stuff covered, and just need like a third letter to fill out your set, then that’s probably a better fit! Another important caveat: an un-tenured professor probably has less credibility than a tenured one. When could a non-research letter go further than an ordinary DWIC? When I’ve seen an individualized project or two that you’ve worked on, and we’ve talked together several times about that project. When I’ve reaching out for help in a new course. If I can speak to you as a TA or tutor as well as a student. If I’m writing a letter for a masters program vs a phd, or a local scholarship vs a national one.</p>

<p>Be sure and let me know (1) what specifically you’re hoping for me to speak to, (2) how I can best help fit into your overall letters and application, and (3) tell me what kind of a program is this for. MS CS ? PhD CS? MFA, DDS, or MBA? A scholarship? It helps to know what I’m aiming for.</p>

<p>It will also help if you can provide:</p>

<ul>
  <li>A transcript</li>
  <li>A résumé/vita, with all of your pertinent info in it</li>
  <li>Anything in particular you’d like me to try and emphasize.</li>
  <li>A(nother) link to the scholarship/program information (and if they have multiple, <em>which</em> one you’re applying to!)</li>
  <li>And also tell me about your other letter writers.</li>
</ul>]]></content><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><summary type="html"><![CDATA[Let’s say you did well in an upper-division class with a professor. You enjoyed the class and also enjoyed learning the material. That might seem like it would make the professor a shoo-in as a good recommender for you for your graduate school application. Here’s why that might not be so! It’s important that someone gives you the full scoop on all this. For instance, if I wrote a student such a letter, I might even be doing them a disservice by taking the slot of someone whose word would go farther.]]></summary></entry><entry><title type="html">Is your shell what you think?</title><link href="https://hemann.pl/shells/" rel="alternate" type="text/html" title="Is your shell what you think?" /><published>2023-03-17T00:00:00+00:00</published><updated>2023-03-17T00:00:00+00:00</updated><id>https://hemann.pl/shells</id><content type="html" xml:base="https://hemann.pl/shells/"><![CDATA[<p>Depending how you installed a new version of <code class="language-plaintext highlighter-rouge">bash</code> on your mac, it
might surprise you to learn which version of bash you’re using. For
instance, if ask for the version of bash at the prompt. Here’s a
reasonable example.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span>bash <span class="nt">--version</span> 
GNU bash, version 5.2.15<span class="o">(</span>1<span class="o">)</span><span class="nt">-release</span> <span class="o">(</span>aarch64-apple-darwin21.6.0<span class="o">)</span>
</code></pre></div></div>

<p>Looks good. If, however, we then go and check the BASH_VERSION …</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$ </span><span class="nb">echo</span> <span class="nv">$BASH_VERSION</span>
3.2.57<span class="o">(</span>1<span class="o">)</span><span class="nt">-release</span>
</code></pre></div></div>

<p>We get a very different and worse number. What gives? Well, your login
shell is using a different version of bash than is currently front on
your path.</p>

<p><a href="login-shell">This SO thread</a> helpfully describes the problem and the
issue. If you are using the new version of homebrew, then you might
have had to move where you keep the programs brew installs: the new
location is <code class="language-plaintext highlighter-rouge">/opt/homebrew/bin</code>. If that’s the one you want to set as
your login shell, then you’ll have to first add it to the
<code class="language-plaintext highlighter-rouge">/etc/shells</code> file, and then use the following to set it.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>chsh -s /opt/homebrew/bin/bash
sudo chsh -s /opt/homebrew/bin/bash
</code></pre></div></div>

<p>(If you failed to add that to your <code class="language-plaintext highlighter-rouge">/etc/shells</code> file, then <code class="language-plaintext highlighter-rouge">chsh</code> will
squawk that this is a non-standard shell.)</p>

<p>But after this, you should be good to go with an up-to-date login shell.</p>]]></content><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><summary type="html"><![CDATA[Depending how you installed a new version of bash on your mac, it might surprise you to learn which version of bash you’re using. For instance, if ask for the version of bash at the prompt. Here’s a reasonable example.]]></summary></entry><entry><title type="html">Job hunting advice.</title><link href="https://hemann.pl/jobs/" rel="alternate" type="text/html" title="Job hunting advice." /><published>2023-02-21T00:00:00+00:00</published><updated>2023-02-21T00:00:00+00:00</updated><id>https://hemann.pl/jobs</id><content type="html" xml:base="https://hemann.pl/jobs/"><![CDATA[<p>I recently had cause to think about some advice for recent graduates
hunting for jobs. This is probably applicable too if you’re laid off
and need to find a new job. Caveats that this worked for me a dozen
years ago, and it’s tailored to students looking for programmer jobs.</p>

<ol>
  <li>Get a second email address that doesn’t obviously look like a
backup email address. You don’t want job mail spam in your inbox
for the rest of eternity.</li>
  <li>Make job hunting your full-time job until you have one. It’s a
9-5 process. There’s a lead time before you’re going to start
seeing results, and you don’t know how long that is, but don’t
sit and wait for that one org of your dreams to get back to you.</li>
  <li>Start with the “usual suspects” job recruiting sites—Monster,
indeed, etc. Fill out profiles and filters and set yourself up
with a feed for job leads.</li>
  <li>Any place that has an RSS feed, find yourself an RSS reader and
subscribe to them. This can be your work list, and mean there’s
a whole bunch of sites you don’t have to check.</li>
  <li>Work backwards in time, from most recent to more distant
postings.</li>
  <li>Niche technology where you have experience can be a good place
to start, because they probably have trouble recruiting for
that.</li>
  <li>When you find a job you want to apply for, do it from the
company’s intranet if you can.</li>
  <li>Google itself also has job search listings now, and you can use
that too.</li>
  <li>Often times when you’re looking up a job listing, you’ll find
another <em>site</em> on which that job is posted that you hadn’t
looked before. This is how you build breadth to your job search.
Let the job listings lead you to more sites, and the job hunt
sites lead you to more jobs listings.</li>
  <li>Recruiters/headhunters can be useful, get in touch w/them too.</li>
  <li>If you’ve graduated recently enough, the school might still
offer advice on your resume. Some schools have an alumni
network that to tap. The other thing I’ve seen is to update
your LinkedIn profile, see where your colleagues and contacts
are working, and then go to those companies’ websites for
listings.</li>
  <li>In fact, you can also start picking out companies that you’ve
heard of that you wouldn’t mind working for, and apply directly
with them. That’s another way to increase your pool of job
sites.</li>
  <li>Use several common search strings for positions on the search
sites. You know that “software engineer” “programmer” “computer
scientist” “software developer” “platform engineer” can bleed
across one another, but the sites don’t and HR might not
either. If a site only lets you have one active search, then
get another email address and another login to that site, and
have your second search set up that way.</li>
  <li>Make sure your resume is AI/search friendly. Use the key words
and phrases that will get you past the automatic filters and
into the hands of a human being.</li>
  <li>Borrow the address of a friend’s couch. If you’re willing to
relocate, and you have someone in the area, then you can put
that person’s address on the resume making you look like a
local hire. “I’ve been staying with a friend out there while I
.. I just recently moved back to my place…” yadda. IME, by the
time you’re far enough along that you seem like a candidate
worth interviewing in person, then that won’t matter. If it
does.</li>
  <li>As I understand it, in person commuting-to-work jobs are going
to be less competitive than the remote jobs, because a) people
place a premium on the work from home and b) you are already
excluding all the people who aren’t in or willing to be in a
commute distance of that job.</li>
  <li>You can and probably should have multiple versions of your
resume tailored to different kinds of positions. Depending on
what technology they want you to work with and what the role
requires, you’ll emphasize different things in your background.
If you can, make it look like you’re already an expert at doing
the job they want to hire you for.</li>
  <li>Double, triple check your emails and spelling and that you’re
addressing the right company with the right job. If you can,
build yourself a little program that will automate portions of
your cover letter. If you’re building it with PDF, then using
TeX macros <code class="language-plaintext highlighter-rouge">\longname</code> <code class="language-plaintext highlighter-rouge">\shortname</code> <code class="language-plaintext highlighter-rouge">\mediumname</code>
<code class="language-plaintext highlighter-rouge">\specialreason</code> to fill in a cover letter could both save you
time and frustration</li>
  <li>Likewise, before you send something, have a tool read it to
you, out loud. You can listen for mistakes that sound obvious
but are difficult to pick out. You can do this on 2x speed if
you’re quick enough. This isn’t a place to be lazy.</li>
  <li>If you have time, especially if you think you’ll be off for a
while, pull out that side project you’d been working on, and
spend some nights and weekends on that. It’s something new you
can show off, looks good on your GitHub commit history, and
gives you something to talk about at an interview.</li>
  <li>This may be less applicable, but <em>if</em> you’re going to be off
work for a <em>while</em> having a volunteer position doing something
related could be helpful just to keep a blank off your resume.
OSS and side projects are nice too, but that doesn’t fit in a
spot on a resume. Maybe start thinking about this in your
second month off, so that you can <em>have</em> something in hand. A
position that you’ve had for 6 months working one day a week
looks better than 5 months off and then a whole month straight
of volunteering.</li>
</ol>

<p>I know <a href="https://youtu.be/KT4vhl8slbE">it’s tough out there</a>. Most of
that is probably common sense to readers, but I hope there’s something
in there that’s worthwhile.</p>]]></content><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><summary type="html"><![CDATA[I recently had cause to think about some advice for recent graduates hunting for jobs. This is probably applicable too if you’re laid off and need to find a new job. Caveats that this worked for me a dozen years ago, and it’s tailored to students looking for programmer jobs.]]></summary></entry><entry><title type="html">Easy SFTPeasy</title><link href="https://hemann.pl/self-signed-certificate/" rel="alternate" type="text/html" title="Easy SFTPeasy" /><published>2022-11-10T00:00:00+00:00</published><updated>2022-11-10T00:00:00+00:00</updated><id>https://hemann.pl/self-signed-certificate</id><content type="html" xml:base="https://hemann.pl/self-signed-certificate/"><![CDATA[<p>The powers that be here at SHU make some webspace available to faculty
and staff here for course-related materials. This is a nice convenient
alternative to the LMS system for webhosting, and I wanted to explore
what else we could do with it. My contact at the Teaching Learning
Center on campus set me up with some space and an account. That I can
use as soon as I can log in and get to it. All technology and know-how
around campus seems geared toward a pointy-clicky Windows based mode
of operation.</p>

<p>Easy command line access requires a little bit of study and
configuration.</p>

<p>Firstly, the right tool for the job.</p>

<h2 id="btw-sftp--ftps">BTW, <code class="language-plaintext highlighter-rouge">SFTP</code> =/= <code class="language-plaintext highlighter-rouge">FTPS</code></h2>

<p>If you don’t know, <code class="language-plaintext highlighter-rouge">SFTP</code> and <code class="language-plaintext highlighter-rouge">FTPS</code> are not the same thing. If a
person talks to you about “secure FTP”, stop that person immediately
and make them clarify to you which they mean. It’s possible that they
don’t know either: trust, but verify.</p>

<p>I found the easy way to connect to our server
(<code class="language-plaintext highlighter-rouge">tltc-web1h-prod.shu.edu</code>) was to simply <code class="language-plaintext highlighter-rouge">brew install lftp</code>. Scads
of dependencies I needed to download and update all handled for me;
man alive package managers are great!</p>

<p>But I run into a problem when I actually try to use this to get to my
space now. I can connect, but when I actually try and move around, you
run into the following error.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ lftp -u hemannja,"$PASSWD" ftp://tltc-web1h-prod.shu.edu/hemannja/
cd: Fatal error: Certificate verification: self-signed certificate ...
</code></pre></div></div>

<h2 id="certificates-and-verification">Certificates and verification</h2>

<p>This error message is saying that the server has a self-signed
certificate authority, and so my system rightly doesn’t trust it. If
this were a nefarious actor, well, it wouldn’t make any sense to take
his own word that he’s an honest fella. But I happen to know that I’m
really dealing with the machine I think I am, so I want to, for this
guy only, accept the certificate that it’s providing me.</p>

<p>So I need to <em>get</em> the certificate that this server is presenting me</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ openssl s_client -connect tltc-web1h-prod.shu.edu:21 -starttls ftp
...
---
Server certificate
-----BEGIN CERTIFICATE-----
123...
...
...456789abcdefgihijklm==
-----END CERTIFICATE-----
subject=...
issuer=...
---
</code></pre></div></div>

<p>I needed to save that certificate itself as a certificate file. Just
the certificate, mind you.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ cat &gt; ~/tltc_web1h_prod_AES-256.crt
...
</code></pre></div></div>

<p>Once I had that, I needed to add it so that <code class="language-plaintext highlighter-rouge">lftp</code> knew about it.</p>

<p>Luckily this was a <a href="https://stackoverflow.com/a/65760327/4355474">solved
problem</a>. If you want to
add it to your MacOS Keychain, <a href="https://support.apple.com/en-my/guide/keychain-access/kyca2431/mac">they also have a help page for
this</a>.</p>]]></content><author><name>Jason Hemann</name><email>jason.hemann@shu.edu</email></author><summary type="html"><![CDATA[The powers that be here at SHU make some webspace available to faculty and staff here for course-related materials. This is a nice convenient alternative to the LMS system for webhosting, and I wanted to explore what else we could do with it. My contact at the Teaching Learning Center on campus set me up with some space and an account. That I can use as soon as I can log in and get to it. All technology and know-how around campus seems geared toward a pointy-clicky Windows based mode of operation.]]></summary></entry></feed>