www.chrismytton.com2024-01-14T06:48:27+00:00https://www.chrismytton.com/index.xmlChris MyttonThis is my personal website where I share anything I find interesting. Follow me on Twitter: @chrismyttonChris MyttonPlain text websites2024-01-14T06:48:27+00:002024-01-14T06:48:27+00:00https://www.chrismytton.com/plain-text-websites/
<p>In a world of web apps, videos, social media and multi-media distraction it’s nice to know there are still some websites out there which use simple plain text.</p>
<p>I came across <a href="https://sjmulder.nl/en/textonly.html">Sijmen J. Mulder’s list of Hyperlinked Text websites</a> the other day, which is a list of websites that mostly use plain old HTML, and skip the JavaScript bloat that has become so common to the web these days.</p>
<p>The sites on this page don’t have loads of adverts, or many images, they’re just simple plain text, and honestly it’s a pleasure reading them. They load fast and you can get straight to the content you’re after without scrolling past adverts and dismissing popups inviting you to provide your email address for dubious purposes.</p>
Chris MyttonIn a world of web apps, videos, social media and multi-media distraction it's nice to know there are still some websites out there which use simple plain textAmazing space facts you might not know2024-01-04T17:43:57+00:002024-01-04T17:43:57+00:00https://www.chrismytton.com/space-facts/
<h2 id="the-surprising-effect-of-altitude-on-sunset-times">The surprising effect of altitude on sunset times</h2>
<p>If you have one observer at sea level, and another observer at 1000m above sea level, who will see the sun set first?</p>
<p>Counterintuitively the observer at 1000m above sea level will see it set first, despite being higher up and therefore seeing further over the horizon. This is due to <a href="https://en.wikipedia.org/wiki/Atmospheric_refraction">atmospheric refraction</a> being reduced at higher altitudes compared to sea level.</p>
<p>Atmospheric refraction is like a cosmic optical illusion. As light from the sun travels through the Earth’s atmosphere, it bends, much like a straw appears to bend in a glass of water. This bending causes the sun to appear slightly higher in the sky than it actually is, delaying the moment of sunset for those at sea level.</p>
<p>This also means that when the Sun’s disc touches the horizon it has actually already set, but you can still see it due to atmospheric refraction. If the atmosphere disappeared at that point then the sun would also disappear!</p>
<h2 id="earth-spins-more-than-360-in-a-day">Earth spins more than 360º in a day</h2>
<p>How many degrees does earth spin in 24 hours?</p>
<p>Without thinking too hard about it I probably would have said 360º, but that’s wrong!</p>
<p>Relative to the stars, Earth actually spins 361º in 24 hours. This is because we’re orbiting the sun, so after spinning 360º we’ve travelled a bit further around the sun, so have to spin an extra 1º to get the sun back to the same point in the sky.</p>
<p><a href="https://commons.wikimedia.org/wiki/File:Sidereal_Day_poster.png#/media/File:Sidereal_Day_poster.png"><img src="https://upload.wikimedia.org/wikipedia/commons/8/8b/Sidereal_Day_poster.png" alt="A sidereal day is 1 Earth rotation relative to the stars; a solar day is 1 Earth rotation relative to the Sun. The Earth rotates 366 times per 'normal' 365-day year relative to the stars, so Earth's sidereal day is 4 minutes shorter than Earth's solar day." height="1200" width="1920" class="not-prose" /></a><small>Image by James O'Donoghue, <a href="https://creativecommons.org/licenses/by/3.0" title="Creative Commons Attribution 3.0">CC BY 3.0</a>, <a href="https://commons.wikimedia.org/w/index.php?curid=132008436">Link</a></small></p>
<p>Related to that, in a “normal” 365 day year, how many full turns do you think the earth makes?</p>
<p>The earth actually makes 366 full turns (relative to the stars) in a 365 day year.</p>
<p>Check out the <a href="https://en.wikipedia.org/wiki/Sidereal_time">Sidereal time Wikipedia entry</a> for a more in-depth explanation of this.</p>
<h2 id="venus-spins-differently-from-other-planets">Venus spins differently from other planets</h2>
<p>Most planets rotate on their axes in an anticlockwise direction as viewed from Earth’s north pole, but Venus rotates clockwise.</p>
<p>This means that Venus is actually spinning “backwards” compared with most<sup id="fnref:other-backwards-spinning-planets" role="doc-noteref"><a href="#fn:other-backwards-spinning-planets" class="footnote" rel="footnote">1</a></sup> other planets in the solar system.</p>
<p>Venus also spins very slowly, which means a sidereal day on Venus (sidereal meaning the time it takes to rotate 360º relative to the stars, 243 Earth days) is longer than a year on Venus (225 Earth days).</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:other-backwards-spinning-planets" role="doc-endnote">
<p>Uranus is the other planet that spins backwards. For added fun it also spins on its side, with an axial tilt of approximately 90º. <a href="#fnref:other-backwards-spinning-planets" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Chris MyttonLearn why sunsets happen earlier at high altitudes, the surprising truth about Earth's daily rotation, and Venus's unique, backward spin.How to query CSVs with SQLite2023-01-23T19:00:00+00:002023-01-23T19:00:00+00:00https://www.chrismytton.com/how-to-query-csvs-with-sqlite/
<p>I’ve got the following shell function in my shell startup file that allows me to quickly query a CSV file with SQL:</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cq<span class="o">()</span> <span class="o">{</span>
<span class="nb">local </span><span class="nv">csv_file</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">1</span>:?Usage:<span class="p"> cq <csv_file> [<table> [<sql>]]</span><span class="k">}</span><span class="s2">"</span> <span class="nv">table</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">2</span><span class="k">:-</span><span class="nv">T</span><span class="k">}</span><span class="s2">"</span> <span class="nv">sql</span><span class="o">=</span><span class="s2">"</span><span class="k">${</span><span class="nv">3</span><span class="k">:-</span><span class="s2">"-interactive"</span><span class="k">}</span><span class="s2">"</span>
sqlite3 :memory: <span class="nt">-header</span> <span class="nt">-csv</span> <span class="nt">-cmd</span> <span class="s2">".import '</span><span class="nv">$csv_file</span><span class="s2">' '</span><span class="nv">$table</span><span class="s2">'"</span> <span class="s2">"</span><span class="nv">$sql</span><span class="s2">"</span>
<span class="o">}</span>
</code></pre></div></div>
<p>I can’t claim to have written this function. I found it on <a href="https://github.com/multiprocessio/dsq/issues/70#issuecomment-1162584448">this GitHub issue thread</a>. I’ve just tweaked it a little to add an optional SQL query argument, and used some more standard SQLite parameters.</p>
<h2 id="usage">Usage</h2>
<p>First argument is the CSV file to query. Second argument is the name of the table to create in the SQLite database. Third argument is the SQL query to run. If you don’t specify a query then you’ll be dropped into an interactive SQLite shell.</p>
<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>cq <<span class="o">(</span>curl <span class="nt">-s</span> https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/vaccinations/vaccinations.csv<span class="o">)</span> <span class="se">\</span>
vax <span class="se">\</span>
<span class="s2">"SELECT location, date, total_vaccinations FROM vax WHERE location = 'United Kingdom' ORDER BY date DESC LIMIT 10;"</span>
</code></pre></div></div>
<h2 id="alternatives">Alternatives</h2>
<p>If you’re looking for something a bit more powerful then there are dozens of tools out there that allow you to query CSV files. Here are a few of the popular ones:</p>
<ul>
<li><a href="https://github.com/multiprocessio/dsq">dsq</a></li>
<li><a href="http://harelba.github.io/q/">q</a></li>
<li><a href="https://github.com/dinedal/textql">textql</a></li>
<li><a href="https://github.com/simonw/sqlite-utils">sqlite-utils</a></li>
</ul>
Chris MyttonA simple shell function to query CSV files using SQLiteWhat land and assets does Cheltenham Council own?2023-01-18T22:00:00+00:002023-01-18T22:00:00+00:00https://www.chrismytton.com/what-land-and-assets-does-cheltenham-council-own/
<h2 id="summary-i-made-a-map-of-the-councils-assets">Summary: I made a map of the council’s assets</h2>
<p>I made a map showing all land and assets owned by Cheltenham Borough Council<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>.</p>
<iframe src="https://www.chrismytton.com/cheltenham-council-land-and-assets/" style="width: 100%; height: 600px; border: none;"></iframe>
<p>You can view it in fullscreen at <a href="https://www.chrismytton.com/cheltenham-council-land-and-assets/">chrismytton.com/cheltenham-council-land-and-assets</a>.</p>
<p>Getting to that point was a fun learning process, and as a bonus I ended up with a free web interface where you can query the data as well!
More details on that below.</p>
<h2 id="data-but-not-very-visual">Data! But not very visual</h2>
<p>I was browsing through <a href="https://www.cheltenham.gov.uk/opendata">Cheltenham Council’s Open data pages</a> the other day and came across their <a href="https://www.cheltenham.gov.uk/info/16/open_data/1190/local_authority_land_and_assets">“Local authority land and assets open data”</a> dataset. From what I understand reading that webpage, the council are required to maintain this list of assets, “in accordance with the mandatory requirements of the local government transparency code”.</p>
<p>The list of assets is provided in XLSX and CSV format. These spreadsheet-like formats are fine to quickly go through if you know what you’re looking for. Not so great for browsing the data in a more visual format.</p>
<p>I’ve been wanting to sharpen my GIS skills for a while, so I got to work processing the data into something that I could plot on a map.</p>
<h2 id="loading-the-data-into-postgis-with-ruby">Loading the data into Postgis with Ruby</h2>
<p>For my first iteration I wanted to use Postgis so I could get more familiar with the various features it offers. I wrote a Ruby script that would take the CSV data, do some mild cleaning on it, load it into Postgis, and then get the data back out of Postgis as GeoJSON.</p>
<p>You can see that script <a href="https://github.com/chrismytton/cheltenham-council-land-and-assets/blob/fd24990/load_csv_and_output_geojson.rb">over on GitHub</a>.</p>
<h2 id="re-projecting-british-national-grid-coordinates-to-gps-coordinates">Re-projecting British National Grid coordinates to “GPS” coordinates</h2>
<p>The coordinates from the CSV’s <code>GeoX</code> and <code>GeoY</code> columns are re-projected in Postgis from EPSG:27700 (British National Grid, which is used on Ordnance Survey maps) to EPSG:4326 (World Geodetic System, which is what GPS uses), which is then stored in the <code>coordinates</code> column. Re-projection isn’t a required step. GeoJSON can handle data with a 27700 projection, and Leaflet can handle 27700 projections, but it requires a 27700 basemap as well. I could get from Ordnance Survey, but it costs money, and I wanted this project to run without an ongoing cost, if possible.</p>
<p>In my experience, working with 4326 data gives you a wider range of options.</p>
<p>This is the SQL I used to re-project the <code>GeoX</code> and <code>GeoY</code> columns and store the result in the <code>coordinates</code> column.</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">UPDATE</span> <span class="n">land_and_assets</span>
<span class="k">SET</span> <span class="n">coordinates</span> <span class="o">=</span> <span class="n">ST_Transform</span><span class="p">(</span>
<span class="n">ST_SetSRID</span><span class="p">(</span>
<span class="n">ST_MakePoint</span><span class="p">(</span><span class="n">GeoX</span><span class="p">,</span> <span class="n">GeoY</span><span class="p">),</span>
<span class="mi">27700</span>
<span class="p">),</span>
<span class="mi">4326</span>
<span class="p">)</span>
</code></pre></div></div>
<h2 id="sql-query-to-get-geojson-out-of-postgis">SQL query to get GeoJSON out of Postgis</h2>
<p>The other thing of note is that I used a fancy Postgis query to get the GeoJSON directly from the database, rather than having to generate it with Ruby.</p>
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">SELECT</span> <span class="n">json_build_object</span><span class="p">(</span>
<span class="s1">'type'</span><span class="p">,</span> <span class="s1">'FeatureCollection'</span><span class="p">,</span>
<span class="s1">'features'</span><span class="p">,</span> <span class="n">json_agg</span><span class="p">(</span><span class="n">feature</span><span class="p">)</span>
<span class="p">)</span> <span class="k">AS</span> <span class="n">geojson</span>
<span class="k">FROM</span> <span class="p">(</span>
<span class="k">SELECT</span> <span class="n">json_build_object</span><span class="p">(</span>
<span class="s1">'type'</span><span class="p">,</span> <span class="s1">'Feature'</span><span class="p">,</span>
<span class="s1">'id'</span><span class="p">,</span> <span class="n">AssetCode</span><span class="p">,</span>
<span class="s1">'geometry'</span><span class="p">,</span> <span class="n">ST_AsGeoJSON</span><span class="p">(</span><span class="n">coordinates</span><span class="p">)::</span><span class="n">json</span><span class="p">,</span>
<span class="s1">'properties'</span><span class="p">,</span> <span class="n">json_build_object</span><span class="p">(</span>
<span class="s1">'organisation'</span><span class="p">,</span> <span class="n">organisation</span><span class="p">,</span>
<span class="s1">'organisationlabel'</span><span class="p">,</span> <span class="n">organisationlabel</span><span class="p">,</span>
<span class="s1">'uprn'</span><span class="p">,</span> <span class="n">uprn</span><span class="p">,</span>
<span class="s1">'assetcode'</span><span class="p">,</span> <span class="n">assetcode</span><span class="p">,</span>
<span class="s1">'propertyname'</span><span class="p">,</span> <span class="n">propertyname</span><span class="p">,</span>
<span class="s1">'streetname'</span><span class="p">,</span> <span class="n">streetname</span><span class="p">,</span>
<span class="s1">'posttown'</span><span class="p">,</span> <span class="n">posttown</span><span class="p">,</span>
<span class="s1">'postcode'</span><span class="p">,</span> <span class="n">postcode</span><span class="p">,</span>
<span class="s1">'tenuretype'</span><span class="p">,</span> <span class="n">tenuretype</span><span class="p">,</span>
<span class="s1">'tenuredetail'</span><span class="p">,</span> <span class="n">tenuredetail</span><span class="p">,</span>
<span class="s1">'holdingtype'</span><span class="p">,</span> <span class="n">holdingtype</span>
<span class="p">)</span>
<span class="p">)</span> <span class="k">AS</span> <span class="n">feature</span>
<span class="k">FROM</span> <span class="p">(</span><span class="k">SELECT</span> <span class="o">*</span> <span class="k">FROM</span> <span class="n">land_and_assets</span><span class="p">)</span> <span class="k">row</span><span class="p">)</span> <span class="n">features</span><span class="p">;</span>
</code></pre></div></div>
<p>Pretty cool that you can conjure up a GeoJSON object using SQL without any further processing.</p>
<p>So this Ruby script worked fine and produced some usable GeoJSON output. You can actually <a href="https://github.com/chrismytton/cheltenham-council-land-and-assets/blob/fd24990e0bbfd9e90d42a0797b6b770e2c63665b/land_and_assets_2021.geojson">view the output directly on GitHub</a> (because GitHub has support for rendering GeoJSON files).</p>
<h2 id="second-attempt-with-gdalogr">Second attempt with GDAL/OGR</h2>
<p>A few weeks later I was working on another project and started to get a deeper understanding and appreciation for the power of <code>ogr2ogr</code> from the GDAL/OGR package. From what I understand GDAL handles raster data, which is essentially geographically-labelled image-type data. OGR handles vector data, which is essentially some kind of coordinates representing points, lines, polygons (and potentially more) in a 2d or 3d space.</p>
<p>This tool allows you to convert between a wide range of vector GIS formats. Among those formats, to my surprise, was CSV! So you can use <code>ogr2ogr</code> to convert a CSV file to GeoJSON directly, without having to load things into Postgres as an intermediate step.</p>
<p>Because <code>ogr2ogr</code> lets you write SQL, we can use this for processing the rows in the input dataset into a format appropriate for the output dataset. In this case replacing some line breaks in a couple of fields, and remapping the names from the CSV to different output names.</p>
<p>You can see the new build script <a href="https://github.com/chrismytton/cheltenham-council-land-and-assets/blob/71d9529327807af23cd750f65e88dc496867c155/bin/build">over on GitHub</a> along with the <a href="https://github.com/chrismytton/cheltenham-council-land-and-assets/blob/main/extract_csv_to_ogr.sql">SQL that’s used for processing</a>.</p>
<p>Moving from the Ruby script to <code>ogr2ogr</code> has dramatically reduced the amount of code needed for this process. Now it’s just one (admittedly quite complex) SQL query and a couple of lines of bash to glue things together.</p>
<h2 id="putting-it-on-a-map">Putting it on a map</h2>
<p>As mentioned GitHub has built in support for viewing GeoJSON files. This used to be powered by Mapbox and was quite slick, but it’s now powered by Azure Maps and is quite frankly inferior to the Mapbox version and a bit clunky.</p>
<p>So I wanted to put the markers on a map that I controlled.</p>
<p>The GeoJSON file was acceptably small once gzipped, so I was happy to just pull in the whole file on page load, rather than having to do any dynamic bounding box queries.</p>
<p>I used Leaflet to display the map, and OpenStreetMap’s tile server for the base map.</p>
<p>You can browse the map by visiting <a href="https://www.chrismytton.com/cheltenham-council-land-and-assets/">chrismytton.com/cheltenham-council-land-and-assets</a>.</p>
<h2 id="exploring-the-data-with-datasette">Exploring the data with Datasette</h2>
<p>Browsing the data visually on the map was the main goal for me, but I still found myself wanting to run some SQL queries on this data.
I wanted to know what the distinct values were in the “Tenure” column.
I wanted to know what kind of holdings there were.
These are things that would be trivial to answer using SQL.</p>
<p>I could have just used <code>ogr2ogr</code>’s built-in SQL to query the data, but this is a bit clunky because it’s designed specifically for looking at geodata.</p>
<p>Enter <a href="https://datasette.io/">Datasette</a>. Specifically <a href="https://lite.datasette.io/">Datasette Lite</a>, which allows you to load in a CSV file from a URL and run SQL against it.</p>
<p>In this case there was some kind of encoding issue with the CSV file which meant it couldn’t be loaded by Datasette Lite directly.
One way to work around this was to convert the CSV to a SQLite database and fix the encodings along the way.</p>
<p>Because I was now using <code>org2ogr</code>, I had everything I needed to convert the CSV data into a multitude of other vector data formats.
A simple <a href="https://github.com/chrismytton/cheltenham-council-land-and-assets/compare/2031fb3...066e170">one line addition</a> to the <code>bin/build</code> script was all it took to output a SQLite database alongside the GeoJSON.</p>
<p>With that change in place it’s now possible to <a href="https://lite.datasette.io/?url=https://github.com/chrismytton/cheltenham-council-land-and-assets/blob/bdf8976e4ba25a6a35174e0597ab3723c47682eb/cheltenham_council_land_and_assets_2021.sqlite">query the data using SQL via Datasette Lite’s web interface</a>, without having to download anything to your computer.</p>
<h2 id="future-enhancements">Future enhancements</h2>
<p>This has definitely been a learning experience! This process follows the classic ETL (Extract, Transform, Load) pattern, but using tools that are specifically designed for geodata. While I’m pleased with how things are working there are definitely some things I’d like to improve.</p>
<ul>
<li>Have a simpler base map than the OpenStreetMap one, which can look a bit cluttered at times</li>
<li>Switch to using Maplibre. It’s fast, it’s fun, and having used Mapbox for a while it’s familiar. It also offers smoother zooming, built-in hash-tracking on location change, as well as changing bearing and angle.</li>
</ul>
<h2 id="links">Links</h2>
<ul>
<li><a href="https://www.chrismytton.com/cheltenham-council-land-and-assets/">Full-screen version of the map</a></li>
<li><a href="https://github.com/chrismytton/cheltenham-council-land-and-assets">GitHub repository</a></li>
<li><a href="https://www.cheltenham.gov.uk/info/16/open_data/1190/local_authority_land_and_assets">Original data</a></li>
<li><a href="https://lite.datasette.io/?url=https://github.com/chrismytton/cheltenham-council-land-and-assets/blob/bdf8976/cheltenham_council_land_and_assets_2021.sqlite">Datasette Lite interface for querying data</a></li>
</ul>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>Well, all the land and assets they are legally required to declare, at least. Some things like operational railways, canals and highways, as well as “assets of national security” are excluded from this list. For more details see the <a href="https://www.gov.uk/government/publications/local-government-transparency-code-2015/local-government-transparency-code-2015#local-authority-land">“Local authority land” section of the Local government transparency code 2015</a>. <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>
Chris MyttonOr how I turned a CSV into a map and a database.Walk from Pittville Pump Room to Prestbury Church2021-09-13T15:14:00+00:002021-09-13T15:14:00+00:00https://www.chrismytton.com/video-walk-from-pittville-pump-room-to-prestbury-church/
<p>Using my 1996 Cheltenham Circular Footpath guidebook as my primary means of navigation I walked a couple of miles along said footpath, from Pittville Pump Room to Prestbury Church.</p>
<p>This part of the route goes around Cheltenham Racecourse and past the Gloucestershire Warwickshire Steam Railway. Along the way I explain a bit about the history of the racecourse and the railway station adjacent to it.</p>
<p>Join me as I get lost, eat blackberries, and read directions from a guidebook.</p>
<div class="youtube-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/HZtq3IrvIVE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
Chris MyttonI walked a couple of miles along Cheltenham Circular Footpath, from Pittville Pump Room to Prestbury Church. This is the video of that walk.Book notes: A Guide to the Good Life2020-11-02T18:00:00+00:002020-11-02T18:00:00+00:00https://www.chrismytton.com/a-guide-to-the-good-life/
<p><strong>Title</strong>: A Guide to the Good Life: The Ancient Art of Stoic Joy</p>
<p><strong>Author</strong>: William B. Irvine</p>
<p><strong>Rating</strong>: 9/10</p>
<p>I read this book a few years ago and have found that following the concepts in it have genuinely helped me to live a more tranquil life.</p>
<p>Read this book if you’re finding the world a bit overwhelming and want to live a more purposeful and fulfilling life, rather than a life of mindless consumption.</p>
<hr />
<h2 id="a-practical-guide-to-modern-stoicism">A practical guide to modern Stoicism.</h2>
<p>Stoicism is a life philosophy that can help you lead a more tranquil and joyous life.</p>
<p>If you embrace the form of Stoicism this book teaches then, according to Seneca (an ancient Stoic), you will “be attended by a constant cheerfulness and a joy that is deep and issues from deep within”.</p>
<h2 id="relation-to-zen-buddhism">Relation to Zen Buddhism</h2>
<p>Stoicism has some things in common with Zen Buddhism.</p>
<p>Both of these philosophies emphasise the transitory nature of the world and the importance of mastering desire.</p>
<p>They also both have tranquility as the ultimate goal and teach us how to get it.</p>
<h2 id="stoic-psychological-techniques">Stoic psychological techniques</h2>
<p>The Stoics developed certain techniques to help them achieve the goal of tranquility.</p>
<h3 id="negative-visualisation">Negative visualisation</h3>
<p>Reflect on the bad things that could happen to you or your loved ones. It makes you grateful for what you have and makes your joy more durable when you are hit by misfortune.</p>
<p>Think about things that make you sad or angry from other people’s perspective.</p>
<h3 id="the-dichotomy-trichotomy-of-control">The Dichotomy (trichotomy) of control</h3>
<ul>
<li>Things we have complete control over, e.g. goals and values</li>
<li>Things we have no control over, e.g. the weather, the news</li>
<li>Things we have some control over, e.g. whether we win at tennis</li>
</ul>
<p>Don’t worry about things you have no control over.</p>
<p>Focus on things you have complete control over.</p>
<p>When it comes to things you have some control over make sure you’re careful setting goals.</p>
<p>Rather than setting the goal of winning the tennis match set the goal of playing to the best of your ability, then be pleasantly surprised if you win.</p>
<h3 id="fatalism">Fatalism</h3>
<p>Let go of the past and the present, they are what fate intended. Focus on the future, which you can control.</p>
<p>This links back to the dichotomy of control. You can’t control the past and present (you can act on the present, but that’s a plan for the future), so don’t spend time worrying about them.</p>
<h3 id="self-denial">Self denial</h3>
<p>Embrace voluntary discomfort occasionally.</p>
<ul>
<li>Underdress for the weather</li>
<li>Eat basic food without herbs and spices</li>
<li>Sleep somewhere uncomfortable</li>
<li>Have black tea or coffee</li>
</ul>
<p>Try and forego opportunities to experience pleasure, they will numb you to it and then you will always need the pleasure to feel tranquil.</p>
<p>By having more discomfort and less pleasure in our lives we widen our comfort zone and learn to appreciate the simpler things in life.</p>
<h3 id="meditation---watching-ourselves-practicing-stoicism">Meditation - Watching ourselves practicing Stoicism</h3>
<p>Write down the days events and how we should have responded to them according to Stoic principles.</p>
<p>Think about the parts where you succeeded and successfully practiced Stoicism.</p>
<ul>
<li>Are you practicing negative visualisation?</li>
<li>Are you distinguishing between things you fully control, partly control and don’t control?</li>
<li>Are you internalising your goals?</li>
<li>Are you dwelling on the past instead of focussing on the future?</li>
<li>Are you practicing self-denial?</li>
</ul>
<h2 id="stoic-advice">Stoic Advice</h2>
<h3 id="duty---on-loving-mankind">Duty - on loving mankind</h3>
<p>Do your duty not through fear of punishment but prospect of reward.</p>
<h3 id="social-relations---on-dealing-with-other-people">Social relations - on dealing with other people</h3>
<ul>
<li>Form your character in private and remain true to it around others</li>
<li>Avoid people with vices you don’t want to pick up</li>
<li>Avoid people that are whiny</li>
<li>Be selective about the social functions you attend</li>
<li>When interacting with annoying people, bear in mind that others find you annoying, too</li>
<li>Don’t worry about what others say, think, or do</li>
<li>Social fatalism: people are the way they are, try to accept them as they are but also try to gently improve them</li>
</ul>
<h3 id="insults---on-putting-up-with-put-downs">Insults - on putting up with put downs</h3>
<p>Pause and consider if what the insulter said is true. If it is then no reason to be upset.</p>
<p>Consider how well informed the insulter is. If you respect the source they are likely trying to help you.</p>
<ul>
<li>Consider the source of insults</li>
<li>We are the source of the sting of an insult</li>
<li>We get upset because of our judgement of “things”</li>
<li>Counter the insult with humour or silence</li>
</ul>
<h3 id="grief---on-vanquishing-tears-with-reason">Grief - on vanquishing tears with reason</h3>
<ul>
<li>Grieve enough to avoid indifference, but not so much that you tend towards madness</li>
<li>Negative visualisation will help remove some of the shock we experience</li>
<li>Retrospective negative visualisation - visualise the past without the person you are grieving and be thankful for the time you had with them</li>
<li>Use reason to make sense of a situation</li>
</ul>
<h3 id="anger---on-overcoming-anti-joy">Anger - on overcoming anti-joy</h3>
<ul>
<li>Being angry is a waste of precious time</li>
<li>Don’t jump to conclusions about people’s motivations</li>
<li>Too much pleasure and not enough self-denial will make us quick to anger</li>
<li>Things that anger us generally don’t do us harm, they are just annoying</li>
<li>Humour can prevent us from becoming angry</li>
<li>Contemplate the impermanence of the world, this will put the thing that made you angry into perspective</li>
<li>When we feel ourselves getting angry, pause to consider the cosmic (in)significance of it</li>
<li>Remind ourselves that our behaviour angers others</li>
<li>When angry, force ourselves to relax our face, soften our voice and slow our pace of walking</li>
<li>If we are unable to control our anger and lash out then we should immediately apologise so that we can return to calm and not obsess over the thing that made us angry</li>
<li>Why experience anti-joy when you can experience joy?</li>
</ul>
<h3 id="personal-values---on-seeking-fame">Personal values - on seeking fame</h3>
<ul>
<li>Remain indifferent to social status</li>
<li>Stoics value their freedom and are reluctant to do anything that gives others power over them</li>
<li>Be indifferent to what other people think so you can keep your freedom</li>
<li>Ignoring what people think of us is another form of not worrying about things we can’t control</li>
</ul>
<h3 id="personal-values---on-luxurious-living">Personal values - on luxurious living</h3>
<ul>
<li>Too much luxury makes it harder to appreciate simple things</li>
<li>Avoid becoming a connoisseur, it will diminish your ability to delight in simple things</li>
</ul>
Chris MyttonThe Ancient Art of Stoic Joy.Be prolific2020-10-22T18:17:00+00:002020-10-22T18:17:00+00:00https://www.chrismytton.com/be-prolific/
<p>There’s a story about an art teacher that split their class in half. They told one half of the students that they’d be graded based on a single piece of work, and the other half that they would be graded on the quantity of work produced.</p>
<p>The half that was being graded on quantity ended up producing higher quality pieces.</p>
<p>By iterating and learning from their mistakes they actually ended up producing better work than the students that only had to produce one piece.</p>
<p>Quantity leads to quality.</p>
<h2 id="share-your-work">Share your work</h2>
<p>Sharing work helps you to think and develop. The feedback you get feeds into the next iteration.</p>
<p>If you’ve enjoyed creating something then there’s a good chance that at least a handful of people in the world will enjoy seeing it or hearing about it.</p>
<p>Promoting yourself and your work can be a good way to clarify your thinking and future direction.</p>
<h2 id="get-better-by-creating-more">Get better by creating more</h2>
<p>Produce lots of stuff and share it.</p>
<p>Being prolific doesn’t mean that everything you produce has to be absolute gold. But the process of producing large quantities of work ultimately leads to a higher quality of work.</p>
<hr />
<p><a href="https://news.ycombinator.com/item?id=24866706" target="_blank">Discussion on Hacker News</a>.</p>
<!--
[Portuguese translation](https://eduardoorige.com.br/posts/seja-prolifico/index.html){:target="_blank"}
[Arabic translation](https://farzat.online/2020/10/28/be-prolific/){:target="_blank"}
-->
Chris MyttonProduce lots of stuff and share it.Hiring an electric scooter2020-10-17T07:06:00+00:002020-10-17T07:06:00+00:00https://www.chrismytton.com/around-town-on-an-e-scooter/
<p>A couple of weeks ago e-scooters started appearing around town. After a bit of research I discovered the local council is running a 12 month e-scooter trial, which they’ve partnered with Zwings for.</p>
<p>So yesterday lunchtime I decided it was time to take one for a spin.</p>
<p>They’re limited to 12mph (20kph), not terribly fast, but still a super fun way to get around.</p>
<div class="youtube-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/S-Q7-BUBw-Q" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
Chris MyttonTesting out the new e-scooters that have appeared around town.Playing tennis after a 4 year hiatus2020-10-15T22:04:00+00:002020-10-15T22:04:00+00:00https://www.chrismytton.com/playing-tennis/
<p>We decided to try out the local tennis courts after not playing since 2016-ish.</p>
<p>Have completely forgotten how to play, but was really good fun. Forgot what an intense workout tennis can be!</p>
<div class="youtube-embed">
<iframe width="560" height="315" src="https://www.youtube.com/embed/Il6SLWAPNKw" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen=""></iframe>
</div>
Chris MyttonHave completely forgotten how to play, but was really good fun. Forgot what an intense workout tennis can be!Ruby: The not so good parts2020-10-11T09:28:00+00:002020-10-11T09:28:00+00:00https://www.chrismytton.com/ruby-the-not-so-good-parts/
<!-- Twitter embed JavaScript -->
<script async="" src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
<p>The other day Nate Berkopec asked this question on Twitter:</p>
<blockquote class="twitter-tweet"><p lang="en" dir="ltr">What is your least favorite part about Ruby?</p>— Nate Berkopec (@nateberkopec) <a href="https://twitter.com/nateberkopec/status/1314235606705340418?ref_src=twsrc%5Etfw">October 8, 2020</a></blockquote>
<p>The thread had some thought provoking responses. I wanted to gather up some of the highlights for posterity and add my own commentary.</p>
<p>In summary:</p>
<ul id="markdown-toc">
<li><a href="#class-variables-are-confusing" id="markdown-toc-class-variables-are-confusing">Class variables are confusing</a></li>
<li><a href="#standard-library-is-too-low-level" id="markdown-toc-standard-library-is-too-low-level">Standard library is too low-level</a></li>
<li><a href="#meta-programming-complexity" id="markdown-toc-meta-programming-complexity">Meta-programming complexity</a></li>
<li><a href="#block-vs-proc-vs-lambda" id="markdown-toc-block-vs-proc-vs-lambda">Block vs proc vs lambda</a></li>
</ul>
<h2 id="class-variables-are-confusing">Class variables are confusing</h2>
<blockquote class="twitter-tweet" data-conversation="none"><p lang="en" dir="ltr">class variables! I can never figure out how they work</p>— Aaron Patterson (@tenderlove) <a href="https://twitter.com/tenderlove/status/1314237029878652928?ref_src=twsrc%5Etfw">October 8, 2020</a></blockquote>
<p>Class variables in Ruby look a bit like instance variables, but they begin with <code>@@</code>.</p>
<p>To confuse matters further Ruby also has class instance variables, which makes searching for documentation confusing if you don’t know what you’re looking for.</p>
<p>This is one of those Ruby features that’s best left alone unless you have a <em>very</em> convincing use case that you can’t <em>possibly</em> solve using anything else.</p>
<h2 id="standard-library-is-too-low-level">Standard library is too low-level</h2>
<blockquote class="twitter-tweet" data-conversation="none"><p lang="en" dir="ltr">Much of the standard library is too low-level, like Net::HTTP, which leads to every Rails app bundling five different third-party HTTP clients.</p>— George Claghorn (@georgeclaghorn) <a href="https://twitter.com/georgeclaghorn/status/1314237870782251008?ref_src=twsrc%5Etfw">October 8, 2020</a></blockquote>
<p>Ruby is definitely showing its age a bit with the standard library. With Ruby 3.0 the standard library is going to become default gems. That might encourage some modernization in the interfaces provided so that more apps can rely on an out-of-the-box solution, rather than bundling their own.</p>
<p>Choice is a good thing. The fact that there are many alternatives to most parts of the standard library is a sign of a healthy ecosystem. But at the same time it would be nice if the libraries Ruby shipped with were good enough for simple scripts without having to pull in external libraries.</p>
<p>For simple HTTP requests when you don’t care about anything other than the body you can always use <code>open-uri</code>.</p>
<div class="language-ruby highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">require</span> <span class="s1">'open-uri'</span>
<span class="n">body</span> <span class="o">=</span> <span class="no">URI</span><span class="p">.</span><span class="nf">open</span><span class="p">(</span><span class="n">url</span><span class="p">).</span><span class="nf">read</span>
</code></pre></div></div>
<p>For anything more complex, you’ll want to turn to a third-party library.</p>
<h2 id="meta-programming-complexity">Meta-programming complexity</h2>
<blockquote class="twitter-tweet" data-conversation="none"><p lang="en" dir="ltr">Meta-programming magic.<br /><br />Makes it awkward to discover where a called method is actually defined. Particularly frustrating for people just learning Ruby.<br /><br />Hopefully tools like sorbet and rbs will help solve the discovery problem to some extent.</p>— Chris Mytton (@chrismytton) <a href="https://twitter.com/chrismytton/status/1314236518790311936?ref_src=twsrc%5Etfw">October 8, 2020</a></blockquote>
<p>My own contribution to the thread.</p>
<p>Ruby has some incredibly powerful and ergonomic metaprogramming features for creating DSLs. Yet this power and ease-of-use leads people to overuse and abuse metaprogramming, leading to hard-to-follow and hard-to-debug programs.</p>
<p>It seems enticing writing a DSL that captures the essence of the problem you’re solving. In reality code that isn’t written in a standard way (i.e. with classes and methods) is harder to understand because you have to understand the DSL before you can understand what the code in the DSL is doing.</p>
<h2 id="block-vs-proc-vs-lambda">Block vs proc vs lambda</h2>
<blockquote class="twitter-tweet" data-conversation="none"><p lang="en" dir="ltr">Block vs proc vs lambda is one of the least elegant bits with the most complexity compared to expressiveness.</p>— Noah Gibbs (@codefolio) <a href="https://twitter.com/codefolio/status/1314237138393858050?ref_src=twsrc%5Etfw">October 8, 2020</a></blockquote>
<p>Ruby has too many ways of creating and calling code closures. There are some subtle differences between the various forms. When faced with a situation where I want to use them I can never remember what the trade-offs between them are.</p>
<p>This isn’t an easy problem to solve, since there’s code out there using all the different forms. It’s one of those things that you get used to, but is a definite sharp edge for people that are new to the language.</p>
<h2 class="no_toc" id="conclusion">Conclusion</h2>
<p>Ruby fared pretty well in this thread, considering it’s a 25 year old language. There are definitely some rough edges, but I still find it to be one of the quickest and most fun ways to go from idea to working code.</p>
<hr />
<h2 class="no_toc" id="im-starting-a-newsletter">I’m starting a newsletter!</h2>
<p>If you’ve enjoyed reading this post then you might also enjoy <a href="/newsletter/">my newsletter</a>, where I’ll be writing about Ruby, software, tech and life.</p>
Chris MyttonShining a light on the dark corners of Ruby.