<?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="http://briandfoy.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="http://briandfoy.github.io/" rel="alternate" type="text/html" /><updated>2026-05-10T07:01:30+00:00</updated><id>http://briandfoy.github.io/feed.xml</id><title type="html">brian d foy</title><subtitle>Random Adventures</subtitle><entry><title type="html">tmux cheatsheet</title><link href="http://briandfoy.github.io/tmux-cheatsheet/" rel="alternate" type="text/html" title="tmux cheatsheet" /><published>2026-01-27T00:00:00+00:00</published><updated>2026-01-27T00:00:00+00:00</updated><id>http://briandfoy.github.io/tmux-cheatsheet</id><content type="html" xml:base="http://briandfoy.github.io/tmux-cheatsheet/"><![CDATA[<p>I guess I’m going to start using tmux because I have to work on a system that
doesn’t have the xterm package and all sorts of things wonky.</p>

<!--more-->

<h2 id="some-commands">Some commands</h2>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ tmux attach -t NAME
$ tmux detach</code></pre></figure>

<h3 id="get-the-session-name">Get the session name</h3>

<p>Get them all:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ tmux ls</code></pre></figure>

<p>Get the current one:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ tmux display-message -p '#S'</code></pre></figure>

<h3 id="rename-session">Rename session</h3>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ tmux rename-session -t OLD NEW</code></pre></figure>

<h2 id="links">Links</h2>

<ul>
  <li><a href="https://tmuxcheatsheet.com">tmux cheatsheet</a></li>
  <li><a href="https://iterm2.com/documentation-tmux-integration.html">tmux in iTerm2</a></li>
  <li><a href="https://github.com/schelcj/scripts/blob/master/smux">Chris Scheller’s tmux config</a></li>
</ul>]]></content><author><name></name></author><summary type="html"><![CDATA[I guess I’m going to start using tmux because I have to work on a system that doesn’t have the xterm package and all sorts of things wonky.]]></summary></entry><entry><title type="html">Skip the first line by using the flip-flop operator</title><link href="http://briandfoy.github.io/skip-the-first-line-by-using-the-flip-flop-operator/" rel="alternate" type="text/html" title="Skip the first line by using the flip-flop operator" /><published>2026-01-17T00:00:00+00:00</published><updated>2026-01-17T00:00:00+00:00</updated><id>http://briandfoy.github.io/skip-the-first-line-by-using-the-flip-flop-operator</id><content type="html" xml:base="http://briandfoy.github.io/skip-the-first-line-by-using-the-flip-flop-operator/"><![CDATA[<p><em>(this was an answer to a StackOverflow question that I didn’t post because it didn’t quite fit, but I use too keen on showing off the flip-flop operator to get rid of it all)</em></p>

<p><em>I also put <a href="https://www.reddit.com/user/briandfoy/comments/1t8o9y1/geeking_out_about_the_flipflop_operator/">this in Reddit</a></em></p>

<p>A few other places:</p>

<ul>
  <li><a href="https://perldoc.perl.org/perlfaq5#How-do-I-change,-delete,-or-insert-a-line-in-a-file,-or-append-to-the-beginning-of-a-file?">perlfaq5: How do I change, delete, or insert a line in a file, or append to the beginning of a file?</a></li>
  <li><a href="https://www.effectiveperlprogramming.com/2010/06/respect-the-global-state-of-the-flip-flop-operator/">Respect the global state of the flip flop operator</a></li>
  <li><a href="https://www.effectiveperlprogramming.com/2010/11/make-exclusive-flip-flop-operators/">Make exclusive flip-flop operators</a></li>
</ul>

<!--more-->

<p>Here’s some text that has a mix of written (“first”) and numeric (“4th”) ordinals, and our task is to change them all to the written form except for the first line:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ cat &gt; file.txt
first line has the numeric ordinal 4th
second line
third is before 4th
4th
next to last
last line is not 4th</code></pre></figure>

<p>Let’s start with a program that simply adds line numbers to the input:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ perl -n -e 'print "$. $_"' file.txt
1 first line has the numeric ordinal 4th
2 second line
3 third is before 4th
4 4th
5 next to last
6 last line is not 4th</code></pre></figure>

<p>We can skip the first line, which means that we only get lines that have a line before it. The <code class="language-plaintext highlighter-rouge">$.</code> is the input line number:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ perl -n -e 'print "$. $_" unless $. == 1' file.txt
2 second line
3 third is before 4th
4 4th
5 next to last
6 last line is not 4th</code></pre></figure>

<p>But we don’t want to skip the first line. We simply don’t want to change it. We can use the same <code class="language-plaintext highlighter-rouge">$.</code> comparison for the part that changes the line. Notice the first line  does not change but the fifth line does:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ perl -n -e 's/4th/fourth/ unless $. == 1; print "$. $_"' file.txt
1 first line has the numeric ordinal 4th
2 second line
3 third is before fourth
4 fourth
5 next to last
6 last line is not fourth</code></pre></figure>

<p>The <code class="language-plaintext highlighter-rouge">-p</code> switch is different than <code class="language-plaintext highlighter-rouge">-n</code> because it outputs the value of the line at the end of the loop (if you change it or not):</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ perl -pe 's/4th/fourth/ unless $. == 1' file.txt
first line has the numeric ordinal 4th
second line
third is before fourth
fourth
next to last
last line is not fourth</code></pre></figure>

<p>Once you know about <code class="language-plaintext highlighter-rouge">$.</code>, you can select lines however you like, such as every other line:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ perl -pe '$_ = uc($_) unless $. % 2' file.txt
first line has the numeric ordinal 4th
SECOND LINE
third is before 4th
4TH
next to last
LAST LINE IS NOT 4TH</code></pre></figure>

<p>Or, even a range (window) of lines:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ perl -pe '$_ = uc($_) unless( $. &lt; 2 or $. &gt; 5)' file.txt
SECOND LINE
THIRD IS BEFORE 4TH
4TH
NEXT TO LAST
last line is not 4th</code></pre></figure>

<p>There’s a special, and sometimes mind-bending, operator that handles this. The <code class="language-plaintext highlighter-rouge">..</code> <a href="https://perldoc.perl.org/perlop#Range-Operators">range operator</a> in scalar context, as it would be in a condition, doesn’t produce a list. It’s the “flip-flop” variant. That returns false until the lefthand argument is true, and then keeps returning true until the righthand argument is true, after which it returns false. It takes many people a few minutes to unravel that if they haven’t used it before:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ perl -pe '$_ = uc($_) if $. == 2 .. $. == 5' file.txt
first line has the numeric ordinal 4th
SECOND LINE
THIRD IS BEFORE 4TH
4TH
NEXT TO LAST
last line is not 4th</code></pre></figure>

<p>Perl, being the language that it is, has special cases for the common cases. You can leave off the explicit comparison because comparing the number to <code class="language-plaintext highlighter-rouge">$.</code> is the default:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ perl -pe '$_ = uc($_) if 2 .. 5' file.txt
first line is not 4th
SECOND LINE
THIRD IS BEFORE 4TH
4TH
NEXT TO LAST
last line is not 4th</code></pre></figure>

<p>As an aside, compare this to sed’s <code class="language-plaintext highlighter-rouge">2,5 ...</code> syntax for a line range.</p>]]></content><author><name></name></author><category term="perl" /><category term="programming" /><category term="flip-flop" /><category term="perlfaq" /><summary type="html"><![CDATA[(this was an answer to a StackOverflow question that I didn’t post because it didn’t quite fit, but I use too keen on showing off the flip-flop operator to get rid of it all) I also put this in Reddit A few other places: perlfaq5: How do I change, delete, or insert a line in a file, or append to the beginning of a file? Respect the global state of the flip flop operator Make exclusive flip-flop operators]]></summary></entry><entry><title type="html">What version of Unicode is your Perl using?</title><link href="http://briandfoy.github.io/what-version-of-unicode-is-your-perl-using/" rel="alternate" type="text/html" title="What version of Unicode is your Perl using?" /><published>2025-12-25T00:00:00+00:00</published><updated>2025-12-25T00:00:00+00:00</updated><id>http://briandfoy.github.io/what-version-of-unicode-is-your-perl-using</id><content type="html" xml:base="http://briandfoy.github.io/what-version-of-unicode-is-your-perl-using/"><![CDATA[<p>Most versions of Perl (v5.38, v5.40, and so on) updates the Unicode Character Database (UCD), and each version of the UCD has new features, often new blocks, characters, or adjustments to property settings. But, which version do you have?</p>

<p>I tried this task a long time ago, in some place I don’t remember, looking at the presence of properties and other things, but I don’t want to create a long chain of things to query to rule out versions. That’s just too much work if I can cheat.</p>

<!--more-->

<p>First, the Unicode data files are embedded in the Perl distribution. Look for them in the *lib/<VERSION>/unicore/* directory inside the perl installation, for example, *lib/5.42.0/unicore/*.</VERSION></p>

<p>The version is in *lib/<VERSION>/unicore/version*, but you don't need to look in that file and it's a bit annoying to construct the path. There is a module in that directory, but you can't use `perldoc -l` to get its path since it contains no pod:</VERSION></p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ perldoc -l unicore::Name
No documentation found for "unicore::Name".</code></pre></figure>

<p>Getting the right path is a bit weird because the path might be a symlink, so I need to get the final path:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ which perl
/Users/brian/bin/perl
$ readlink -f $(which perl)
/usr/local/perls/perl-5.42.0/bin/perl5.42.0
$ readlink -f $(which perl) | xargs perl -e '$ARGV[0] =~ s|bin/perl([^/]+)\z|lib/$1/unicore/version|; print scalar &lt;&lt;&gt;&gt;'
16.0.0</code></pre></figure>

<p>That works, but is a bit annoying. There’s another way that took me a minute to discover; buried in <a href="https://metacpan.org/pod/Unicode::UCD">Unicode::UCD</a> is the <code class="language-plaintext highlighter-rouge">UnicodeVersion</code> function, all the way back to v5.8:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ perl -MUnicode::UCD -E 'say Unicode::UCD::UnicodeVersion()'
16.0.0
$ perl5.8.9 -MUnicode::UCD -e 'print Unicode::UCD::UnicodeVersion()'
5.1.0</code></pre></figure>

<p>Finally, if you screw around with the stuff in <em>unicore/</em>, perhaps in an attempt to upgrade the UCD version in a perl installation, that’s on you. Don’t do that.</p>

<p>Now here’s the same thing for other languages:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">$ python3 --version
Python 3.10.6
$ python3 -c 'import unicodedata; print(unicodedata.unidata_version)'
13.0.0

$ ruby --version
ruby 3.2.2 (2023-03-30 revision e51014f9c0) [arm64-darwin22]
$ ruby -e 'require "rbconfig"; puts RbConfig::CONFIG["UNICODE_VERSION"]'
15.0.0</code></pre></figure>

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

<ul>
  <li><a href="https://stackoverflow.com/a/6942982/2766176">tchrist’s answer to “How to identify programmatically in Java which Unicode version supported?”</a></li>
</ul>]]></content><author><name></name></author><category term="perl" /><category term="programming" /><category term="unicode" /><category term="ruby" /><category term="python" /><summary type="html"><![CDATA[Most versions of Perl (v5.38, v5.40, and so on) updates the Unicode Character Database (UCD), and each version of the UCD has new features, often new blocks, characters, or adjustments to property settings. But, which version do you have? I tried this task a long time ago, in some place I don’t remember, looking at the presence of properties and other things, but I don’t want to create a long chain of things to query to rule out versions. That’s just too much work if I can cheat.]]></summary></entry><entry><title type="html">Is that Perl module still alive?</title><link href="http://briandfoy.github.io/is-that-perl-module-still-alive/" rel="alternate" type="text/html" title="Is that Perl module still alive?" /><published>2025-12-17T00:00:00+00:00</published><updated>2025-12-17T00:00:00+00:00</updated><id>http://briandfoy.github.io/is-that-perl-module-still-alive</id><content type="html" xml:base="http://briandfoy.github.io/is-that-perl-module-still-alive/"><![CDATA[<p>How do you determine if a Perl module is “alive”? This question from <a href="https://www.reddit.com/r/perl/comments/1por46u/poe_module_still_alive/">/r/perl</a>, and I think it deserves a thoughtful answer. I did <a href="https://www.reddit.com/r/perl/comments/1por46u/comment/num52ex/">answer in that thread</a>, but I thought I’d expand that.</p>

<!--more-->

<p>These ideas apply to any software, but the particulars are for Perl. The question was specifically about the <a href="https://metacpan.org/pod/POE">POE</a> module.</p>

<p>Consider what I typically look at when I’m evaluating a module. Although we are looking at POE, this is the same sort of evaluation you might go through with a distro, judging based on your context how much these matter to you.</p>

<h2 id="is-there-a-more-alive-alternative">Is there a more alive alternative?</h2>

<p>Is there any other way to do this that’s current and maintained? For example, I “maintain” Net::SSH::Perl, which I paid another contractor (who paid yet another contractor) to write for one of my clients. But, people should use <a href="https://metacpan.org/pod/Net::OpenSSH">Net::OpenSSH</a>.</p>

<p>Part of this is to not be fooled by release dates, since minor things, such as a <em>LICENSE</em> or <em>SECURITY.md</em> file to bring the distro up to current standards, doesn’t make the code any more recent. Have the releases included substantive changes or bugfixes?</p>

<p>My goal is that all of my distros pass the same basic checks so I can maintain them in bulk. If one gets a new file, like <em>SECURITY.md</em>, they all get that file (as appropriate).</p>

<h2 id="how-old-is-the-current-release-and-whats-the-velocity">How old is the current release, and what’s the velocity?</h2>

<p>The last release of POE was at the end of 2022. Some things are just done and don’t need new releases; Dominus’s <a href="https://perl.plover.com/yak/12views/samples/notes.html#sl-9">remarks on his Template module</a> are interesting. I don’t think POE is in that bucket.</p>

<p>The most recent merged pull request is from 2019, and that’s a Pod fix. The last substantive merged pull request is from 2015. (<a href="https://github.com/rcaputo/poe/pulls?q=is%3Apr+is%3Aclosed">closed pull requests</a>)</p>

<h2 id="what-are-the-open-issues">What are the open issues?</h2>

<p>On <a href="https://metacpan.org/">MetaCPAN</a>, you can look at the number of open issues in the left menu. POE still uses the sunsetted <a href="https://rt.cpan.org">rt.cpan.org</a> (red flag), and there hasn’t been a maintainer reply to an opened issue since Dec 2022 there (and even later in the GitHub pull requests).</p>

<p>Likewise, the “Testers” link to CPAN Testers is helpful. Even if a module is maintained and has repeated failures on your platform across several versions, it’s not really “maintained” for your purposes.</p>

<p>The GitHub repo, <a href="https://github.com/rcaputo/poe">rcaputo/poe</a> has open pull requests, most of which are simple usability fixes for the distro.</p>

<h2 id="who-is-involved">Who is involved?</h2>

<p>Some of the people involved with POE are the sort to adopt modules merely to keep the lights on. If you see my name (on a module I didn’t invent myself), Todd Rinaldo, and a few others, it’s a sign that the module has reached the point where no one is interested in fixing it.</p>

<p>Indeed, I have more than a few modules that I keep alive (usually at the request of clients) but have never used, never substantially edited, and have no idea how they work. I will apply fixes if they work and make sense, so I’m really just a janitor.</p>

<h2 id="are-those-people-active">Are those people active?</h2>

<p>Along with that, you can look at the activity of any of the comaintainers of a module. In this case, that’s BERGMAN, APOCAL, BINGOS, BSMITH, CFEDDE, HACHI, MARTIJN, and XANTUS. But, I only know that because I am a CPAN author, looked into PAUSE, and used “View Permissions”. (a comaintainer is merely a person who is allowed to upload a new release). Without a PAUSE account, you can use <a href="https://cpanmeta.grinnz.com/perms">cpanmeta.grinnz.com</a>.</p>

<p>Beware, though, that their most recently uploaded release in the PAUSE data files might not be their most recent activity. Say BINGOS released a new POE yesterday, and CFEDDE then did that again today, you wouldn’t immediately see BINGO’s activity. However, MetaCPAN shows you prior versions, the uploader, and the date in the “Jump to version” pull-down. RCAPUTO made a release in 2015, BINGOS revived it in 2020, and then there’s the 2022 release.</p>]]></content><author><name></name></author><category term="perl" /><category term="pause" /><category term="cpan" /><summary type="html"><![CDATA[How do you determine if a Perl module is “alive”? This question from /r/perl, and I think it deserves a thoughtful answer. I did answer in that thread, but I thought I’d expand that.]]></summary></entry><entry><title type="html">Compiling Perl v5.8 on Debian Slim</title><link href="http://briandfoy.github.io/compiling-perl-v5-8-on-debian-slim/" rel="alternate" type="text/html" title="Compiling Perl v5.8 on Debian Slim" /><published>2025-11-29T00:00:00+00:00</published><updated>2025-11-29T00:00:00+00:00</updated><id>http://briandfoy.github.io/compiling-perl-v5-8-on-debian-slim</id><content type="html" xml:base="http://briandfoy.github.io/compiling-perl-v5-8-on-debian-slim/"><![CDATA[<p>I need a container for Perl v5.8, and I want it to be as small as I can make it. Starting with <a href="https://hub.docker.com/layers/library/debian/bookworm-slim/images/sha256-993f5593466f84c9200e3e877ab5902dfc0e4a792f291c25c365dbe89833411f">debian:bookworm-slim</a>, I’d download the v5.8 source, modify it with <a href="https://metacpan.org/pod/Devel::PatchPerl">patchperl</a>, then compile it. But, there were problems.</p>

<!--more-->

<p>First, some modules don’t compile. Notably, <a href="https://metacpan.org/pod/HTML::TagSet">HTML::TagSet</a> changed its minimal version to v5.10, although <a href="https://metacpan.org/pod/LWP">LWP</a>, which aims for v5.8.1, depends on it. Then <a href="https://metacpan.org/pod/Test::Fatal">Test::Fatal</a> updated its minimum version to v5.12, although <a href="https://metacpan.org/pod/URI">URI</a>, which <a href="https://metacpan.org/pod/LWP">LWP</a> also needs. Neither of these changes were necessary.</p>

<p>Fixing the modules was easy enough by installing old versions before I start anything else:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">cpanm PETDANCE/HTML-TagSet-3.20.tar.gz RJBS/Test-Fatal-0.017.tar.gz</code></pre></figure>

<p>The <a href="https://metacpan.org/pod/parent">parent</a> module had some trouble which I didn’t bother to figure out. v5.8 didn’t like something in the tests, so I installed it without running the tests:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">cpanm --notest parent</code></pre></figure>

<p>Then I got weird errors trying to install <a href="https://metacpan/pod/IO::Socket::SSL">IO::Socket::SSL</a>. This one was really weird. The docker build died with a suspicious error:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">#9 42.94 DIED. FAILED tests 1-15
#9 42.94    Failed 15/15 tests, 0.00% okay
#9 42.94 t/alpn............................FAILED tests 1-5
#9 43.00    Failed 5/5 tests, 0.00% okay
#9 43.00 t/auto_verify_hostname............$!=No such file or directory, $@=IO::Socket::SSL: Bad protocol 'tcp', S$SSL_ERROR=IO::Socket::INET configuration failed at t/auto_verify_hostname.t line 34.</code></pre></figure>

<p>This one was a bit tricky, and the change between v5.8 and v5.10 does not show up in the <em>perldelta</em>. After trying lots of different things, I search for <code class="language-plaintext highlighter-rouge">Bad protocol 'tcp'</code>. That’s a result of the slim version of a base image that does not distribute <em>/etc/protocols</em>. That file defines the numeric constants for the Internet protocols, including TCP and UDP. If that file is not there, <code class="language-plaintext highlighter-rouge">getprotobynam</code> can’t resolve the names to their constants.</p>

<p>This wasn’t a problem in v5.10 because <a href="https://metacpan.org/pod/Socket">Socket</a> defined those constants itself and didn’t need <code class="language-plaintext highlighter-rouge">getprotobynam</code>. I solved this by creating the file myself:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">printf "tcp 6 TCP\nudp 17 UDP\n" &gt;&gt; /etc/protocols</code></pre></figure>

<p>I could also get these by installing the data package that has the files:</p>

<figure class="highlight"><pre><code class="language-text" data-lang="text">apt-get install netbase</code></pre></figure>]]></content><author><name></name></author><category term="perl" /><category term="containers" /><category term="debian" /><category term="slim" /><summary type="html"><![CDATA[I need a container for Perl v5.8, and I want it to be as small as I can make it. Starting with debian:bookworm-slim, I’d download the v5.8 source, modify it with patchperl, then compile it. But, there were problems.]]></summary></entry><entry><title type="html">A tiny Mojolicious server in a test program</title><link href="http://briandfoy.github.io/a-tiny-mojolicious-server-in-a-test-program/" rel="alternate" type="text/html" title="A tiny Mojolicious server in a test program" /><published>2025-08-12T00:00:00+00:00</published><updated>2025-08-12T00:00:00+00:00</updated><id>http://briandfoy.github.io/a-tiny-mojolicious-server-in-a-test-program</id><content type="html" xml:base="http://briandfoy.github.io/a-tiny-mojolicious-server-in-a-test-program/"><![CDATA[<p><em>I originally wrote this quickly to see about posting to my reddit profile: <a href="https://www.reddit.com/user/briandfoy/comments/1modg3c/a_tiny_mojo_server_in_a_test_program/">the post</a></em></p>

<p>Sometimes I need a simple web server to do something in a test. Most often this web server needs to simulate some error condition, such as a timeout or other server error.</p>

<!--more-->

<p>In this test program I fork. In the child, I start a Mojolicious server where the <code class="language-plaintext highlighter-rouge">/</code> path has a 10 second delay. This will be longer than the time-out I set for the user-agent in the parent. The magic is the <code class="language-plaintext highlighter-rouge">Mojo::IOLoop-&gt;start</code> that keeps the server going at the end of the program.</p>

<p>In the parent I do whatever I want to test, which is usually something much more complicated. When I’m done, I shut down the server and get on in life.</p>

<figure class="highlight"><pre><code class="language-perl" data-lang="perl"><span class="c1">#!perl</span>
<span class="k">use</span> <span class="nv">v5</span><span class="mf">.40</span><span class="p">;</span>

<span class="k">use</span> <span class="nn">Mojolicious::</span><span class="nv">Lite</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">Mojo::Server::</span><span class="nv">Daemon</span><span class="p">;</span>
<span class="k">use</span> <span class="nn">Mojo::</span><span class="nv">UserAgent</span><span class="p">;</span>

<span class="k">my</span> <span class="nv">$port</span> <span class="o">=</span> <span class="mi">3000</span><span class="p">;</span>

<span class="k">my</span> <span class="nv">$pid</span> <span class="o">=</span> <span class="nb">fork</span><span class="p">;</span>

<span class="k">if</span><span class="p">(</span> <span class="nv">$pid</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">)</span> <span class="p">{</span>
    <span class="nb">local</span> <span class="nv">$SIG</span><span class="p">{</span><span class="nv">INT</span><span class="p">}</span> <span class="o">=</span> <span class="k">sub </span><span class="p">{</span><span class="nb">exit</span><span class="p">};</span>
    <span class="nb">local</span> <span class="nv">$SIG</span><span class="p">{</span><span class="nv">TERM</span><span class="p">}</span> <span class="o">=</span> <span class="k">sub </span><span class="p">{</span><span class="nb">exit</span><span class="p">};</span>
    <span class="nb">local</span> <span class="nv">$SIG</span><span class="p">{</span><span class="bp">__WARN__</span><span class="p">}</span> <span class="o">=</span> <span class="k">sub </span><span class="p">{</span><span class="mi">1</span><span class="p">};</span>

    <span class="nv">get</span> <span class="p">'</span><span class="s1">/</span><span class="p">'</span> <span class="o">=&gt;</span> <span class="k">sub </span><span class="p">($c) {</span>
      <span class="nb">sleep</span> <span class="mi">10</span><span class="p">;</span>
      <span class="nv">$c</span><span class="o">-&gt;</span><span class="nv">render</span><span class="p">(</span><span class="s">text</span> <span class="o">=&gt;</span> <span class="p">'</span><span class="s1">Hello from inside the program!</span><span class="p">');</span>
    <span class="p">};</span>

    <span class="nb">local</span> <span class="nv">*STDOUT</span><span class="p">;</span>
    <span class="nb">open</span> <span class="bp">STDOUT</span><span class="p">,</span> <span class="p">'</span><span class="s1">&gt;&gt;</span><span class="p">',</span> <span class="p">'</span><span class="s1">/dev/null</span><span class="p">';</span>
    <span class="k">my</span> <span class="nv">$app</span> <span class="o">=</span> <span class="nv">app</span><span class="o">-&gt;</span><span class="nb">log</span><span class="p">(</span> <span class="nn">Mojo::</span><span class="nv">Log</span><span class="o">-&gt;</span><span class="k">new</span><span class="p">(</span><span class="s">path</span> <span class="o">=&gt;</span> <span class="p">'</span><span class="s1">/dev/null</span><span class="p">')</span> <span class="p">);</span>
    <span class="k">my</span> <span class="nv">$daemon</span> <span class="o">=</span> <span class="nn">Mojo::Server::</span><span class="nv">Daemon</span><span class="o">-&gt;</span><span class="k">new</span><span class="p">(</span><span class="s">app</span> <span class="o">=&gt;</span> <span class="nv">$app</span><span class="p">,</span> <span class="s">listen</span> <span class="o">=&gt;</span> <span class="p">["</span><span class="s2">http://127.0.0.1:</span><span class="si">$port</span><span class="p">"]);</span>
    <span class="nv">$daemon</span><span class="o">-&gt;</span><span class="nv">start</span><span class="p">;</span>
    <span class="nn">Mojo::</span><span class="nv">IOLoop</span><span class="o">-&gt;</span><span class="nv">start</span><span class="p">;</span>
    <span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
    <span class="nb">sleep</span> <span class="mi">2</span><span class="p">;</span> <span class="c1"># let server start</span>
    <span class="k">my</span> <span class="nv">$ua</span> <span class="o">=</span> <span class="nn">Mojo::</span><span class="nv">UserAgent</span><span class="o">-&gt;</span><span class="k">new</span><span class="o">-&gt;</span><span class="nv">inactivity_timeout</span><span class="p">(</span><span class="mi">3</span><span class="p">);</span>
    <span class="k">my</span> <span class="nv">$tx</span> <span class="o">=</span> <span class="nv">$ua</span><span class="o">-&gt;</span><span class="nv">get</span><span class="p">(</span> <span class="p">"</span><span class="s2">http://127.0.0.1:</span><span class="si">$port</span><span class="s2">/</span><span class="p">"</span> <span class="p">);</span>

    <span class="k">if</span><span class="p">(</span> <span class="nb">eval</span> <span class="p">{</span><span class="nv">$tx</span><span class="o">-&gt;</span><span class="nv">result</span><span class="p">}</span> <span class="p">)</span> <span class="p">{</span>
        <span class="nv">say</span> <span class="p">"</span><span class="s2">BODY: </span><span class="p">"</span> <span class="o">.</span> <span class="nv">$tx</span><span class="o">-&gt;</span><span class="nv">res</span><span class="o">-&gt;</span><span class="nv">body</span><span class="p">;</span>
        <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="nv">say</span> <span class="p">"</span><span class="s2">ERROR: $@</span><span class="p">";</span>
        <span class="p">}</span>

    <span class="nb">kill</span> <span class="mi">9</span><span class="p">,</span> <span class="nv">$pid</span><span class="p">;</span>
    <span class="nb">waitpid</span> <span class="nv">$pid</span><span class="p">,</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span></code></pre></figure>

<p>Previously, I also used <a href="https://github.com/libwww-perl/HTTP-Daemon">HTTP::Daemon</a>, which was fine too, but I often am dealing with Mojolicious so it’s already there.</p>]]></content><author><name></name></author><category term="programming" /><category term="perl" /><category term="mojolicious" /><summary type="html"><![CDATA[I originally wrote this quickly to see about posting to my reddit profile: the post Sometimes I need a simple web server to do something in a test. Most often this web server needs to simulate some error condition, such as a timeout or other server error.]]></summary></entry><entry><title type="html">Remove album ratings from Apple Music</title><link href="http://briandfoy.github.io/remove-album-ratings-from-apple-music/" rel="alternate" type="text/html" title="Remove album ratings from Apple Music" /><published>2025-06-06T00:00:00+00:00</published><updated>2025-06-06T00:00:00+00:00</updated><id>http://briandfoy.github.io/remove-album-ratings-from-apple-music</id><content type="html" xml:base="http://briandfoy.github.io/remove-album-ratings-from-apple-music/"><![CDATA[<p>Apple’s iTunes, a long time ago, had a ratings feature for albums from back in its SoundJam MP days, which turned into predictive album ratings. Where track ratings are shown as red stars, the album ratings are show as black stars (and predictive ratings as grey stars).</p>

<!--more-->

<p>This is normally not a problem, but the album rating is somehow lumped into the song rating for smart playlists. And, there’s no longer a way to make or remove album ratings. Yet, those ancient data are still tracked and there’s a listing column for album ratings.</p>

<p>The trick is to set the album rating to something that is the same as zero stars. The album rating is actually a number between 1 and 100 that is mapped onto five stars. Give it something that maps to zero stars; <code class="language-plaintext highlighter-rouge">1</code> is good:</p>

<figure class="highlight"><pre><code class="language-plain" data-lang="plain">tell application "Music"
	repeat with theTrack in selection
		set album rating of theTrack to 1
	end repeat
end tell</code></pre></figure>]]></content><author><name></name></author><category term="apple" /><category term="applescript" /><category term="programming" /><category term="music" /><category term="ratings" /><summary type="html"><![CDATA[Apple’s iTunes, a long time ago, had a ratings feature for albums from back in its SoundJam MP days, which turned into predictive album ratings. Where track ratings are shown as red stars, the album ratings are show as black stars (and predictive ratings as grey stars).]]></summary></entry><entry><title type="html">Let’s talk about beavers, but we can’t ever look at one</title><link href="http://briandfoy.github.io/let-s-talk-about-beavers-but-we-can-t-ever-look-at-one/" rel="alternate" type="text/html" title="Let’s talk about beavers, but we can’t ever look at one" /><published>2025-05-17T00:00:00+00:00</published><updated>2025-05-17T00:00:00+00:00</updated><id>http://briandfoy.github.io/let-s-talk-about-beavers-but-we-can-t-ever-look-at-one</id><content type="html" xml:base="http://briandfoy.github.io/let-s-talk-about-beavers-but-we-can-t-ever-look-at-one/"><![CDATA[<p>On an outing, a couple of companions mentioned that they were reading <em>Beaver Land</em> by Leila Philip. They remarked about several curious facts about beavers, including a claim I found dubious: that beavers could direct the trees they felled. That is, the beavers could decide then execute a plan that would make the tree fall in a direction they chose. Of course, to anyone who does this, as I do, that’s obviously crap.</p>

<!--more-->

<p>Think about how beavers chew away at a tree—they chew away at tree until the top portion of the tree either falls over or is connected by a dull point of the connected fibers of the heartwood. These beavers aren’t directing the fall at all; they are merely taking what comes to them.</p>

<p>Along with this claim was that beavers have some sort of special hearing that allows them to hear the tree fibers cracking so they know when it’s going to fall, and they can escape safely. Anyone who has been in this situation knows that you don’t need special hearing, and you don’t even need to be particularly close. The fibers snapping is loud.</p>

<p>So, I went to look for the research, and I found the distressing article <a href="https://kb.osu.edu/server/api/core/bitstreams/a5564996-7238-550a-871c-0688d4852c42/content">The Orientation of Beavers (Castor canadensis) when Cutting Trees</a> (OHIO J SCI 103 (5):143-146, 2003).</p>

<p>This entry isn’t about beavers; it’s about the idea that you can have an idea of beaver behavior without ever looking at one, or putting forth a way to predict what would happen.</p>

<p>Again, science is the art of prediction. Can I observe the world, detect patterns, and make correct predictions? In this case, given a tree, can I predict to a high degree where a beaver will stand?</p>

<p>I suspect you can’t. I suspect the beaver stands wherever he was when he encountered the tree, and the direction of travel is much more predictive than anything else. I also expect that beavers don’t really have a plan; they act compulsively and randomly. But it’s up to someone else to prove through a mountain of evidence that that’s not true.</p>

<h2 id="beavers-die">Beavers die</h2>

<p>One of this group’s assertions was that beavers had a special power to avoid being hurt by trees. Of course, that’s stupid and false:</p>

<blockquote>
  <p>The idea that beavers can control the direction of a tree’s fall is now out of favor based on two types of observations: 1) many trees get caught in the foliage of other trees, and 2) falling trees sometimes kill beavers</p>
</blockquote>

<p>But this statement is similarly stupid. That a tree gets caught up doesn’t mean that the beaver didn’t control the direction. In a dense forest, any direction is likely to cause a hang-up. And, that falling trees sometimes kill beavers doesn’t mean beavers didn’t control the direction. That can be “shit happens”, that there are stupid beavers (choosing a bad direction is still choosing), or nothing at all.</p>

<p>There’s nothing here that comes from actual observations of beavers, and as such, saying anything about how beavers orient themselves to the trees is just the bunk.</p>

<h2 id="the-elephant-and-the-three-blind-men">The elephant and the three blind men</h2>

<p>In the parable of the three blind men and the elephant, three different people feel different, select parts of an elephant then tell others what an elephant looks like, having never touched all of an elephant. And, as the parable’s situation forces, having never <em>seen</em> an elephant.</p>

<p>So, let’s suppose that we want to discover if beavers employ a plan and execute it with skill to repeatedly and consistently succeed. What’s the first thing we’d want to do?</p>

<p>If I were designing this experiment, I’d want a bunch of graduate students, volunteers, and field workers to observe beavers and record what they do. I’d interview as many people as I could about beaver behavior. If I could, I’d ask beavers what they are up to.</p>

<p>This paper goes in a different direction. It ignores the beavers and looks at the tree stumps.</p>

<p>Wait, what? How is a tree stump going to tell them anything? Here’s the glaring red flag that shows that the authors not only don’t know anything about beavers, they don’t know anything about trees.</p>

<h2 id="directional-felling">Directional felling</h2>

<p>In the world of forestry, “directional” felling refers specifically to making the tree lay over in a direction against its prevailing forces. For example, suppose the tree in front of me leans to the left. Can I fall it to the right instead?</p>

<p>To accomplish that, you can’t do what beavers do: chew away the tree until the remaining fibers fail. Most forestry directional felling is going to maintain some of the tree’s fibers and use those fibers to constrain the tree from falling in the disfavored directions. This is the “hinge”, and typically it’s about 80% of the diameter of the tree and slightly in front of the center of the tree.</p>

<p>Even during the fall, the hinge is intact until the face-cut aspects meet, stopping the free fall of the tree and causing the momentum of the falling weight to rip the hinge apart. This typically happens when the angle of the bole (the main part of the tree) is around 45 degrees to 30 degrees to the ground, in which case the direction of fall is a <em>fait accompli</em>. Indeed, in some falls, these fibers never completely fail because the tree’s branches hold the bole off the ground.</p>

<p>But, I don’t think the people involved in this paper have ever had to cut down a tree, or at least one large enough they might lose their life if they did it incorrectly.</p>

<h2 id="the-stump-is-not-the-tree">The stump is not the tree</h2>

<p>Earlier, I wrote about <a href="https://briandfoy.github.io/indirect-measures-and-bad-conclusions/">indirect measures</a> and that most science is not actually a serious attempt to add to human knowledge. That is the case here as well. There is nothing in this paper that increases our understanding of beavers. We already know the paper did not observe any beavers. They also didn’t observe any trees that were still standing.</p>

<p>Instead, their indirect measure was the disconnected stump left over and the orientation of surrounding trees. From that, and only that, they try to work backward to what the beaver was thinking and what the beaver did. They use a plumb line to measure the angle of the trunk compared to straight up, and assume that they know something based on that. If you’ve ever worked in a forest, you know this is complete bullshit. Trees do all sorts of crazy things, and knowing what the stump looks like doesn’t tell you about the bole. The angle at knee height is useless. But it is something that you can measure.</p>

<p>Consider, for example, how the weight is distributed in the canopy. A tree leaning to the right might have its center of gravity to the left because there are more or heavier branches on the left. These things matter, but aren’t part of the data.</p>

<p>One curious thing about this paper is that it doesn’t mention the tree type. But why would you if a tree is just a tree? Some trees, like birch, are thin and tall without branching, so canopy asymmetry doesn’t matter as much as it does with a pine. <a href="https://ohiodnr.gov/go-and-do/plan-a-visit/find-a-property/alum-creek-state-park">The Ohio Department of Natural Resources says the forest is a mix of beech and maple</a>.</p>

<p>With all of this, the paper lacks any science (predictive power) and any investigation of the beaver’s intent. If all the trees are leaning toward that marshland or pond, the beavers might not have any agency at all in the direction that the tree falls.</p>

<p>But there’s this nonsense:</p>

<blockquote>
  <p>Far from the shore, the trees tend to be fairly symmetrical and vertical (Loehle 1986). Thus, trees should fall in the direction from which they are cut.</p>
</blockquote>

<p>This is stupidly beyond belief; it’s unsupported, has no observational evidence, and is a completely broken syllogism.</p>

<p>It is there only to lead to the next statement:</p>

<blockquote>
  <p>Therefore, beavers trying to control the direction trees fall should cut from the side nearest the water to minimize the distance the trunk or branches need to be dragged.</p>
</blockquote>

<h2 id="how-they-could-have-done">How they could have done</h2>

<p>They could have recorded beavers chewing on trees and watching them fall. They wouldn’t have to measure 452 stumps, make large unsupported logical leaps,</p>

<p>But the acknowledgments give it away:</p>

<blockquote>
  <p>This research was funded by the Howard Hughes Medical Institute Undergraduate Biological Sciences Education Program grant to Ohio Wesleyan University.</p>
</blockquote>

<p>This is a vanity project to keep students, who are short-term workers, busy. They didn’t care to get to the truth; they cared about a short-term project that could satisfy a grant and be completed by temporary workers.</p>]]></content><author><name></name></author><category term="science" /><category term="beavers" /><summary type="html"><![CDATA[On an outing, a couple of companions mentioned that they were reading Beaver Land by Leila Philip. They remarked about several curious facts about beavers, including a claim I found dubious: that beavers could direct the trees they felled. That is, the beavers could decide then execute a plan that would make the tree fall in a direction they chose. Of course, to anyone who does this, as I do, that’s obviously crap.]]></summary></entry><entry><title type="html">Counting IPv4 addresses with a bitmap</title><link href="http://briandfoy.github.io/counting-ipv4-addresses-with-a-bitmap/" rel="alternate" type="text/html" title="Counting IPv4 addresses with a bitmap" /><published>2025-04-13T00:00:00+00:00</published><updated>2025-04-13T00:00:00+00:00</updated><id>http://briandfoy.github.io/counting-ipv4-addresses-with-a-bitmap</id><content type="html" xml:base="http://briandfoy.github.io/counting-ipv4-addresses-with-a-bitmap/"><![CDATA[<p>I had  this task to count IPv4 addresses, but the typical way of counting with a hash has problems. With Perl, you might think to just use each item as the hash key and increment its value each time you find one. However, when you are looking at most of the IPv4 space, or even half of it, that’s a couple billion keys that might not fit into memory. Remember, each of those values are a scalar value (SV) that take up several bytes merely by existing, and that SV takes up several octets.</p>

<!--more-->

<p>As an aside, hash keys are not SVs; they are simple strings. This is why a hash key cannot be tainted and one way to get around taint checking is to send the SV through a hash key, losing all its SV flags, and pull it back out. I write about this in <a href="https://www.masteringperl.org">Mastering Perl</a>.</p>

<p>Maybe you have enough memory to do all of this, but in the environment where I do this, I do not. That’s simply because the owners of the hardware aren’t going to give me several hundred Gigs of RAM. So, I need to come up with another way.</p>

<p>Since I only needed to know if a particular IPv4 address was part of the data, I don’t really care about which address that was (but I will be able to). I just need the count of unique IPv4 addresses. Perl has a way that I can map them onto a much smaller data structure: the bit vector.</p>

<p>But IP numbers are just just, well, numbers (sometimes represented as
strings), so let’s simplify this with a smaller set of numbers. I’ll use one
bit for each IP address, and it’s position is its integer value. For example, the address 192.168.1.1 is really the positive whole number 3,232,235,777, which I know because I have a bash shell alias to convert it:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ alias ip_aton
alias ip_aton='perl -MSocket=inet_aton -le '\''print unpack q(N), inet_aton(shift)'\'''

$ ip_aton 192.168.1.1
3232235777
</code></pre></div></div>

<p>I won’t go through a long <code class="language-plaintext highlighter-rouge">vec</code> tutorial here, especially since our field width is one bit which makes it straightforward. Here’s a small demonstration where I start with nothing in <code class="language-plaintext highlighter-rouge">$bitmap</code>, and when I see a number, I set a bit at that position. Perl takes care of extending the bitmap as needed. I use numbers up to 13:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>#!perl
use v5.36;
use strict;
use warnings;

my $bitmap;
foreach my $i ( random_numbers(10, 4) ) {
	say "Saw $i";
	vec( $bitmap, $i, 1 ) = 1;
	say show_vec($bitmap);
	}

sub random_numbers ($max = 13, $n = 10) {
	my @a = map { int rand $max } 1 .. $n;
	}

sub show_vec ($b) {
	my $bits = 8 * length $b;
	my $s =
		join '',
		map { vec($b, $_, 1) ? '+' : '.' }
		0 .. $bits - 1;
	}
</code></pre></div></div>

<p>Here’s one run. In the bitvector, I show “not set” as <code class="language-plaintext highlighter-rouge">.</code> and “set” as <code class="language-plaintext highlighter-rouge">+</code>. Notice that Perl grows the size of the bitvector as needed:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code> 2: ..+.....
 9: ..+......+......
 2: ..+......+......
 8: ..+.....++......
</code></pre></div></div>

<p>Now I want to know haw many of the unique numbers I saw, so I add a function to count the bits:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>sub count_unpack ($b) {
	unpack("%32b*", $b);
	}
</code></pre></div></div>

<p>Now honestly, raise your hand if you’ve ever seen something like that before. I think I’ve only seen it because I handled the <code class="language-plaintext highlighter-rouge">pack</code> section
of the latest <em>Programming Perl</em>.</p>

<p>The <code class="language-plaintext highlighter-rouge">%</code> in the <code class="language-plaintext highlighter-rouge">unpack</code> indicates that I want a checksum of the values, taking 32 thingys at a time. This is effectively the count of set bits.</p>

<p>Now, if instead of random numbers I do this for IP addresses, I get the count of IPv4 addresses, and do it for something under 600 MB (counting all the other stuff going on). That’s pocket change for some applications.</p>

<p>As a side note, <code class="language-plaintext highlighter-rouge">unpack</code> has a nybble order. It doesn’t matter for this task because it does not change the count, but I could play games with where the bits show up. This is endianness at the octet level:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>$ perl -le 'print unpack q(B8), shift' p
01110000

$ perl -le 'print unpack q(b8), shift' p
00001110
</code></pre></div></div>]]></content><author><name></name></author><category term="perl" /><category term="programming" /><category term="bit-vector" /><category term="vec" /><summary type="html"><![CDATA[I had this task to count IPv4 addresses, but the typical way of counting with a hash has problems. With Perl, you might think to just use each item as the hash key and increment its value each time you find one. However, when you are looking at most of the IPv4 space, or even half of it, that’s a couple billion keys that might not fit into memory. Remember, each of those values are a scalar value (SV) that take up several bytes merely by existing, and that SV takes up several octets.]]></summary></entry><entry><title type="html">Everything is not miscellaneous</title><link href="http://briandfoy.github.io/everything-is-not-miscellaenous/" rel="alternate" type="text/html" title="Everything is not miscellaneous" /><published>2025-03-01T00:00:00+00:00</published><updated>2025-03-01T00:00:00+00:00</updated><id>http://briandfoy.github.io/everything-is-not-miscellaenous</id><content type="html" xml:base="http://briandfoy.github.io/everything-is-not-miscellaenous/"><![CDATA[<p>A friend suggested I read <em>Everything is Miscellaneous</em> after I was telling him my efforts to clean up the metadata in my music collection. Even aside from the idea of a genre, what year was this music track published? Is what I have the single, the album version, the radio music, a bootleg, a live track, or something else? I like to find all the covers of songs, so I can</p>

<!--more-->

<p>So, let’s summarize the book. It’s really easy because most of the text is the sort of fluff that has taken over these sorts of non-fiction books where the author is a major part of the story. It’s entertaining when Hunter Thompson does it, but no one is Hunter Thompson, so stop it.</p>

<p>There are different ways to organize objects and information about objects. In the physical world, objects take up space and thus exclude other objects from that space (and give me a moment to say the same about the “digital” world), The author calls that the <em>First Order</em>. After that, a large enough collection benefits from a way to find that part of physical space that holds the object I want. That’s <em>Second Order</em>, and might be a card catalog, for example. Beyond that, there’s an exciting new <em>Third Order</em> that the digital world gives us where we “tag” things.</p>

<p>This book was published in 2007, so it was likely written a year or two earlier. For comparison, Gmail launched in April 2004 with the idea that there should be no folders because search makes that useless. Later, in 2010, Gmail had to essentially add folders through a kludge with <a href="https://gmail.googleblog.com/2010/04/new-in-labs-nested-labels-and-message.html">nested labels</a>. It’s easy to see why this had to happen:</p>

<ul>
  <li>searching all of your email for a common term returns too many results</li>
  <li>people have one or two major concepts of an email, such as “Duck Project” and “Mallard”, and don’t want all emails that match “duck”, “project”, or “mallard”</li>
  <li>tags are folders</li>
</ul>

<p>Let’s go back to the difference between physical and digital objects. Each takes up space somehow. Each needs some way to look up the space it takes. Each of those “Second  Order” organizations can list as many ways as they like to lead the searcher to that address. There is really no third order, digital-only technique. Tagging objects is just another card catalog.</p>

<p>Librarians already know this. There can be a card catalog for the subject, author, or title. These fulfill different starting points in increasing order of specificity:</p>

<ul>
  <li>I don’t know what object I want, but I know its subject</li>
  <li>I know the author and want to find their books</li>
  <li>I know the title I want</li>
</ul>

<p>The mistake that people make about “digital” is that it is something new. It’s not. We can do all of these things in physical space with 3x5 cards. It would be a lot of work and require a lot of people and many, many index cards, but you can do it. It would take up a lot of space.</p>

<p>All of that space disappears inside the computer because the representation of an index card doesn’t exist until I ask for it, and it doesn’t even really exist then. The physical dimensions disappear.</p>

<p>In the computer world, each time someone tags an object, the virtual card catalogs can immediately update. Instead of a couple of tags, we can have two hundred, two thousand, or two million.</p>

<p>This is especially advantageous when the tags are automatic, which might allow many more tags than we’d ever imagine. Consider an image recognition step that can recognize a bridge over a river next to a mountain. The cataloging system can remember all things and allow for things like vector databases that remember multiple attributes, and we can look for all the images with bridges and mountains, even if there is no river.</p>

<p>This is a different sort of searching, though, and it’s mildly interesting. Given even small to moderate scale, looking for an image with particular elements will return too many results. Again, we’re back at the Gmail problem. With too many objects, random searching will fail. For example, choose your favorite photo and imagine you tag it with everything in it. Now, how many of those tags do you have to specify to find that photo among all the others that have the same tags? And how many hours are you going to devote to adding enough tags that there is some unique way to find it?</p>

<p>But we have another way, and the book goes off the rails about this when the author claims that a digital object cannot be in more than one folder. Of course it can. That some systems don’t do it is not a statement of possibility. In Unix, for instance, there’s the hard link, named “hard” because it’s literally just another name for the same data on disk. You can have as many of these as you like.</p>

<p>But let’s back up a moment. A folder isn’t really a folder in the sense of a file cabinet, but rather a skeuomorphic thing that tries to represent a familiar concept in meatspace. Files don’t exist in folders. Instead, pointers to data exist in “folders”, and there’s no requirement that there can’t be multiple pointers to the same data. The computer does not care. The author misunderstands this and uses his misunderstanding to make his case for tagging. Folders are just tags that have a list of files with that tag, just like Gmail had to admit.</p>

<p>This is a completely different topic from people being confused about folders and labels. “Folders” are typically displayed with icons that look like physical file folders in the graphical UI, and people navigate through them. Each folder can have more folders, and this roughly creates a tree for an organization that the person chooses based on what is important to them. See <a href="https://johnnydecimal.com">johnnydecimal</a> for example.</p>

<p>But, it’s not really a tree; it’s more like a graph because a folder can contain links to folders in other trees outside of it, and even above it. Consider for example, I have a folder in my macOS Desktop named “Good Things”, under which I have another folder named “mac Things”, and in that folder I put a link to Desktop again. I follow that interior “Desktop” and see the folder “Good Things” again, and click through the folders infinitely without ever getting to the end.</p>

<h2 id="some-random-notes">Some random notes</h2>

<ul>
  <li>The Dewey Decimal system isn’t bad because it’s Western. The particular categories might not reflect social needs today, but the categories aren’t the system.</li>
  <li>Any classification system, including tags, will have social problems. Some group is not going to like it. There is no balanced tree that will satisfy the world and the diversity of values.</li>
  <li>Some of the confusion is that an assigned number, no matter the system, corresponds to physical space. That’s one way to do it, but imagine a completely different system now where every object is easily discovered by RFID and is randomly shelved. When that book is requested, the RFID system locates it and presents it. Consider the difference in grocery stores before and after <a href="https://www.smithsonianmag.com/smart-news/bizarre-story-piggly-wiggly-first-self-service-grocery-store-180964708/">Piggly Wiggly let shoppers collect their own groceries</a> directly off the shelves. In the before times, the shopper didn’t need to know how to find the item, and in the after times, they did.</li>
</ul>]]></content><author><name></name></author><category term="books" /><summary type="html"><![CDATA[A friend suggested I read Everything is Miscellaneous after I was telling him my efforts to clean up the metadata in my music collection. Even aside from the idea of a genre, what year was this music track published? Is what I have the single, the album version, the radio music, a bootleg, a live track, or something else? I like to find all the covers of songs, so I can]]></summary></entry></feed>