kokada.dev.atom.xml - sfeed_tests - sfeed tests and RSS and Atom files
HTML git clone git://git.codemadness.org/sfeed_tests
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
kokada.dev.atom.xml (494691B)
---
1 <?xml version="1.0" encoding="utf-8"?>
2 <feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>kokada</title><link href="https://kokada.dev" rel="alternate"/><link href="https://kokada.dev/atom/" rel="self"/><id>https://kokada.dev</id><updated>2024-12-04T00:00:00+00:00</updated><subtitle/><entry><title>Praise to scdoc to generate man pages</title><link href="https://kokada.dev/blog/praise-to-scdoc-to-generate-man-pages/" rel="alternate"/><published>2024-12-04T00:00:00+00:00</published><id>https://kokada.dev/blog/praise-to-scdoc-to-generate-man-pages/</id><summary type="html"><p>Hey, its been a long time since my <a href="posts/2024-10-07/01-enabling-le-audio-lc3-in-wf-1000xm5.md">last blog
3 post</a>. It is mostly
4 because I ran out of things to write, but I expected this. This is probably
5 more likely how I am actually going to post from now. At least, it shows that
6 my plan to have a <a href="https://kokada.dev/blog/making-a-blog-for-the-next-10-years">blog for a long
7 time</a>, that is
8 easy to go back when I wanted is working fine, but I digress.</p>
9 <p>Going back to the theme of the today blog post, I needed to write a <a href="https://en.wikipedia.org/wiki/Man_page">man
10 page</a> for the first time in years. I
11 hate <a href="https://en.wikipedia.org/wiki/Troff">troff</a>, the typesetting system used
12 for man pages (similar to <a href="https://en.wikipedia.org/wiki/LaTeX">LaTeX</a> for
13 documents). It is one of the weirdest languages that I ever saw, and even the
14 example in Wikipedia shows that:</p>
15 <pre><code class="language-troff">.ND &quot;January 10, 1993&quot;
16 .AU &quot;Ms. Jane Smith&quot;
17 .AT &quot;Upcoming appointment&quot;
18 .MT 5
19 .DS
20 Reference #A12345
21 .sp 4
22 Mr. Samuel Jones
23 Field director, Bureau of Inspections
24 1010 Government Plaza
25 Capitoltown, ST
26 .sp 3
27 Dear Mr. Jones,
28 .sp 2
29 .P
30 Making reference to the noted obligation to submit for state inspection our newly created production process, we request that you consider the possible inappropriateness of subjecting the innovative technologies of tomorrow to the largely antiquated requirements of yesterday. If our great state is to prosper in the twenty-first century, we must take steps
31 .B now ,
32 in
33 .I this
34 year of
35 .I this
36 decade, to prepare our industrial base for the interstate and international competition that is sure to appear. Our new process does precisely that. Please do not let it be undone by a regulatory environment that is no longer apt.
37 .P
38 Thank you for your consideration of our position.
39 .FC Sincerely
40 .SG
41 </code></pre>
42 <p>Keep in mind that the break lines are necessary every time you introduce a
43 macro, like <code>.I this</code> (that I <em>think</em> it is for italics). Yes, this format is
44 as illegible as hell, and it is worse that the format lacks good tooling (or at
45 least I didn't find any good ones).</p>
46 <p>Most people when they need to write a man page nowadays ends up using some
47 other format that generates a man page. For example, in the past I used
48 <a href="https://pandoc.org/">Pandoc</a> to convert Markdown to a man page, but even if
49 Pandoc is a great project the result is sub-optimal at best: Markdowns are, at
50 the end, designed for generating HTML (and a subset of it), and not man pages,
51 so you basically ends up fighting the format for it to do what you want.
52 Also, Pandoc is a big project, with a ~200MB binary (at least it is the default
53 Pandoc binary in Nix).</p>
54 <p>For this specific project I needed something small. I am trying to replace one
55 of the most essential pieces inside NixOS, <code>nixos-rebuild</code>, written in Bash,
56 with a <a href="https://discourse.nixos.org/t/nixos-rebuild-ng-a-nixos-rebuild-rewrite/55606/">full rewritten in
57 Python</a>
58 (sorry Rust zealots!), called <code>nixos-rebuild-ng</code>.</p>
59 <p>Since this project will eventually (if successful) be in the critical path for
60 NixOS, I want to reduce the number of dependencies as much as possible, so
61 something as big as Pandoc is out. I could use
62 <a href="https://asciidoc.org/">AsciiDoc</a>, but it is a big complicated Python project
63 (this may seem ironic, but <code>nixos-rebuild-ng</code> has only one runtime dependency,
64 that is optional). And I also hated the last time I tried to use it to generate
65 man pages: it more flexible than Markdown, but still far from optimal.</p>
66 <p>Thanks to Drew DeVault (creator of <a href="https://swaywm.org/">SwayWM</a>) that seems it
67 had the same issues in the past and created
68 <a href="https://drewdevault.com/2018/05/13/scdoc.html"><code>scdoc</code></a>, a very simple man
69 page generator using a DSL inspired in Markdown, but specific to generate man
70 pages. The binary is written in C (and advantage in this case since it means it
71 is easier to bootstrap), is small (~1 Kloc) and has no dependencies, so it
72 fits the requirement.</p>
73 <p>While the language suffers from being a niche project for a niche segment, the
74 <a href="https://man.archlinux.org/man/scdoc.5.en">man page</a> for it is actually really
75 nice. It is terse though and lacks examples, and this is what this blog post
76 will try to accomplish.</p>
77 <p>To start, let's have a quick summary of the syntax, written in <code>scdoc</code> as
78 comments:</p>
79 <pre><code class="language-scdoc">; quick summary:
80 ; # new section
81 ; comments starts with ;
82 ; - this is a list
83 ; - sub-list
84 ; - *bold*: _underline_, force a line break++
85 ; - [tables], \[ can be used to force an actual [
86 ; . numbered list
87 ; please configure your editor to use hard tabs
88 ; see `man 5 scdoc` for more information about syntax
89 ; or https://man.archlinux.org/man/scdoc.5.en
90 </code></pre>
91 <p>I actually added this summary in the <code>.scd</code> (the <code>scdoc</code> extension) files that
92 I wrote, so it is easy for someone that never saw the format to start
93 collaborating.</p>
94 <p>And here an example of a (summarised) man page in <code>.scd</code> format:</p>
95 <pre class="chroma"><code><span class="line"><span class="cl">nixos-rebuild-ng(8)
96 </span></span><span class="line"><span class="cl">
97 </span></span><span class="line"><span class="cl"><span class="gh"># NAME
98 </span></span></span><span class="line"><span class="cl"><span class="gh"></span>
99 </span></span><span class="line"><span class="cl">nixos-rebuild - reconfigure a NixOS machine
100 </span></span><span class="line"><span class="cl">
101 </span></span><span class="line"><span class="cl"><span class="gh"># SYNOPSIS
102 </span></span></span><span class="line"><span class="cl"><span class="gh"></span>
103 </span></span><span class="line"><span class="cl"><span class="ge">_nixos-rebuild_</span> \[--upgrade] [--upgrade-all]++
104 </span></span><span class="line"><span class="cl"> \[{switch,boot}]
105 </span></span><span class="line"><span class="cl">
106 </span></span><span class="line"><span class="cl"><span class="gh"># DESCRIPTION
107 </span></span></span><span class="line"><span class="cl"><span class="gh"></span>
108 </span></span><span class="line"><span class="cl">This command has one required argument, which specifies the desired operation.
109 </span></span><span class="line"><span class="cl">It must be one of the following:
110 </span></span><span class="line"><span class="cl">
111 </span></span><span class="line"><span class="cl"><span class="ge">*switch*</span>
112 </span></span><span class="line"><span class="cl"> Build and activate the new configuration, and make it the boot default.
113 </span></span><span class="line"><span class="cl"> That is, the configuration is added to the GRUB boot menu as the
114 </span></span><span class="line"><span class="cl"> default menu entry, so that subsequent reboots will boot the system
115 </span></span><span class="line"><span class="cl"> into the new configuration. Previous configurations activated with
116 </span></span><span class="line"><span class="cl"> nixos-rebuild switch or nixos-rebuild boot remain available in the GRUB
117 </span></span><span class="line"><span class="cl"> menu.
118 </span></span><span class="line"><span class="cl">
119 </span></span><span class="line"><span class="cl"><span class="ge">*boot*</span>
120 </span></span><span class="line"><span class="cl"> Build the new configuration and make it the boot default (as with
121 </span></span><span class="line"><span class="cl"> <span class="ge">*nixos-rebuild switch*</span>), but do not activate it. That is, the system
122 </span></span><span class="line"><span class="cl"> continues to run the previous configuration until the next reboot.
123 </span></span><span class="line"><span class="cl">
124 </span></span><span class="line"><span class="cl"><span class="gh"># OPTIONS
125 </span></span></span><span class="line"><span class="cl"><span class="gh"></span>
126 </span></span><span class="line"><span class="cl"><span class="ge">*--upgrade, --upgrade-all*</span>
127 </span></span><span class="line"><span class="cl"> Update the root user&#39;s channel named &#39;nixos&#39; before rebuilding the
128 </span></span><span class="line"><span class="cl"> system.
129 </span></span><span class="line"><span class="cl">
130 </span></span><span class="line"><span class="cl"> In addition to the &#39;nixos&#39; channel, the root user&#39;s channels which have
131 </span></span><span class="line"><span class="cl"> a file named &#39;.update-on-nixos-rebuild&#39; in their base directory will
132 </span></span><span class="line"><span class="cl"> also be updated.
133 </span></span><span class="line"><span class="cl">
134 </span></span><span class="line"><span class="cl"> Passing <span class="ge">*--upgrade-all*</span> updates all of the root user&#39;s channels.
135 </span></span><span class="line"><span class="cl">
136 </span></span><span class="line"><span class="cl">See the Nix manual, <span class="ge">*nix flake lock --help*</span> or <span class="ge">*nix-build --help*</span> for details.
137 </span></span><span class="line"><span class="cl">
138 </span></span><span class="line"><span class="cl"><span class="gh"># ENVIRONMENT
139 </span></span></span><span class="line"><span class="cl"><span class="gh"></span>
140 </span></span><span class="line"><span class="cl">NIXOS_CONFIG
141 </span></span><span class="line"><span class="cl"> Path to the main NixOS configuration module. Defaults to
142 </span></span><span class="line"><span class="cl"> <span class="ge">_/etc/nixos/configuration.nix_</span>.
143 </span></span><span class="line"><span class="cl">
144 </span></span><span class="line"><span class="cl"><span class="gh"># FILES
145 </span></span></span><span class="line"><span class="cl"><span class="gh"></span>
146 </span></span><span class="line"><span class="cl">/etc/nixos/flake.nix
147 </span></span><span class="line"><span class="cl"> If this file exists, then <span class="ge">*nixos-rebuild*</span> will use it as if the
148 </span></span><span class="line"><span class="cl"> <span class="ge">*--flake*</span> option was given. This file may be a symlink to a
149 </span></span><span class="line"><span class="cl"> flake.nix in an actual flake; thus <span class="ge">_/etc/nixos_</span> need not be a
150 </span></span><span class="line"><span class="cl"> flake.
151 </span></span><span class="line"><span class="cl">
152 </span></span><span class="line"><span class="cl"><span class="gh"># AUTHORS
153 </span></span></span><span class="line"><span class="cl"><span class="gh"></span>
154 </span></span><span class="line"><span class="cl">Nixpkgs/NixOS contributors
155 </span></span></code></pre><p>And here is a screenshot of the result:</p>
156 <p><a href="https://github.com/thiagokokada/blog/raw/main/posts/2024-12-04/2024-12-04-230955_hyprshot.png"><img src="https://github.com/thiagokokada/blog/raw/main/posts/2024-12-04/2024-12-04-230955_hyprshot.png" alt="Man page rendered from scd
157 file"></a></p>
158 <p>One of nice things that I found is how looking at the plain text looks kind
159 like the man page result already. And if you know Markdown, you can basically
160 understand most things that is happening. There are a few differences, like
161 <code>*bold*</code> instead of <code>**bold**</code>, and while they're unfortunate they're not the
162 end of the world.</p>
163 <p>Now, the format has its quirks. The first line being the name of the program
164 and section in parenthesis is required, but this makes sense, since you need
165 this information for the corners. But for one, it requires the usage of hard
166 tabs to create indentation, and the error messages are awful, in a situation
167 that kind remembers me of <code>Makefile</code>. Also the choice of <code>[</code> to start a table
168 means that the traditional <code>app [command]</code> needs in many cases to be escaped as
169 <code>app \[command]</code>. I found this a strange choice since this is supposed to be a
170 format that is only used for man pages, and using <code>[command]</code> to indicate an
171 optional is common, but at least it is easy to escape.</p>
172 <p>In the end, I think all that matters is the result. And for the first time for
173 all those years trying to write a man page, I am satisfied with the result. The
174 man page looks exactly as I wanted once rendered, and the <code>.scd</code> file looks
175 reasonable good that it can work as a documentation for someone that for one
176 reason or another can't use the man page (can't say the same for the troff
177 version). Also, it is really easy for someone to just go there and update the
178 man page, even without experience in the format (except for maybe the
179 requirement of tabs). So all in all, I really liked the format, and will use it
180 again if I need to write another man page in the future.</p></summary></entry><entry><title>Enabling LE Audio/LC3 in WF-1000XM5</title><link href="https://kokada.dev/blog/enabling-le-audio-lc3-in-wf-1000xm5/" rel="alternate"/><published>2024-10-07T00:00:00+00:00</published><id>https://kokada.dev/blog/enabling-le-audio-lc3-in-wf-1000xm5/</id><summary type="html"><p>One of things that I hate the most about the fact that we are all using
181 wireless earbuds instead of wired earphones is the latency: it is bad, getting
182 up to seconds(!) depending on your particular combination of OS/earbuds/device.</p>
183 <p>There is a solution though: Bluetooth LE Audio, that is supposed to fix
184 multiple issues with the original design for Bluetooth Classic Audio, including
185 a much lower latency, improved efficiency (e.g.: less battery power) and even
186 multiple streams of audio. LE Audio also includes a new default codec for
187 improved audio quality, <a href="https://en.wikipedia.org/wiki/LC3_(codec)">LC3</a>, that
188 replaces the venerable <a href="https://en.wikipedia.org/wiki/SBC_(codec)">SBC</a> codec
189 for audio.</p>
190 <p>However, the standard is a mess right now: a few wireless headphones already
191 support it, but they're generally disabled by default and it is pretty messy to
192 enable. And even after enabling it, getting it to work can be a pain.</p>
193 <p>I have pretty much the best setup to use LE Audio right now: a recently
194 released Pixel 9 Pro with Sony's
195 <a href="https://www.sony.ie/headphones/products/wf-1000xm5">WF-1000XM5</a> earbuds, and
196 after lots of tries I got it to work. You can see below the versions of
197 everything I am using:</p>
198 <ul>
199 <li>Android: 14</li>
200 <li><a href="https://play.google.com/store/apps/details?id=com.sony.songpal.mdr">Sound
201 Connect</a>:
202 11.0.1</li>
203 <li>WM-1000XM5: 4.0.2</li>
204 </ul>
205 <p>The first thing you need to do is enable in &quot;Sound Connect&quot; app &quot;LE Audio
206 Priority&quot; in &quot;Device Settings -&gt; System&quot;:</p>
207 <p><a href="https://github.com/thiagokokada/blog/raw/main/posts/2024-10-07/photo_4909454744305642922_y.jpg"><img src="https://github.com/thiagokokada/blog/raw/main/posts/2024-10-07/photo_4909454744305642922_y.jpg" alt="LE Audio option inside Sound
208 Connect"></a></p>
209 <p>After this, you will need to pair your headset with the device again. You can
210 do this as same as always: press and hold the button in case for a few seconds
211 until a blue light starts to blink. However, this is where things starts to get
212 janky: I couldn't get the headset to pair with Android again.</p>
213 <p>A few of the things that I needed to do (in no specific order):</p>
214 <ul>
215 <li>Remove the previous paired headset</li>
216 <li>Restart the Android</li>
217 <li>Clean-up &quot;Sound Connect&quot; storage (Long press the app icon -&gt; &quot;App info&quot; -&gt;
218 &quot;Storage and Cache&quot; -&gt; &quot;Clear storage&quot;)</li>
219 </ul>
220 <p>If you can get the headset to connect, go to the &quot;Bluetooth&quot; settings in
221 Android, click in the gear icon for the headset and enable &quot;LE Audio&quot; option:</p>
222 <p><a href="https://github.com/thiagokokada/blog/raw/main/posts/2024-10-07/photo_4909454744305642937_y.jpg"><img src="https://github.com/thiagokokada/blog/raw/main/posts/2024-10-07/photo_4909454744305642937_y.jpg" alt="LE Audio option Bluetooth
223 Settings"></a></p>
224 <p>If you can't, you may want to <a href="https://helpguide.sony.net/mdr/2963/v1/en/contents/TP1000783925.html">restore the headset to factory
225 settings</a>
226 and try again from the start (that means pairing your device with &quot;Sound
227 Connect&quot; again, and you may want to try to clear the storage before doing so).</p>
228 <p>Yes, the process is extremely janky, but I think this is why both &quot;Sound
229 Connect&quot; and Android marks this feature as beta/experimental. And I still need
230 to test the latency, but from my initial testing there are some glitches when
231 the audio is only used for a short period of time (e.g.: Duolingo only enables
232 the audio when the character is speaking). So I only recommend this if you want
233 to test how LE Audio will behave, since it is clear that this needs more
234 polish.</p></summary></entry><entry><title>Quick bits: where does Nix store Flake's trusted settings?</title><link href="https://kokada.dev/blog/quick-bits-where-does-nix-store-flakes-trusted-settings/" rel="alternate"/><published>2024-09-30T00:00:00+00:00</published><id>https://kokada.dev/blog/quick-bits-where-does-nix-store-flakes-trusted-settings/</id><summary type="html"><p>Have you ever run a <code>nix build</code> command and had this prompt?</p>
235 <pre class="chroma"><code><span class="line"><span class="cl"><span class="gp">$</span> nix run .#darwinActivations/Sekai-MacBook-Pro
236 </span></span><span class="line"><span class="cl"><span class="go">do you want to allow configuration setting &#39;extra-substituters&#39; to be set to &#39;https://nix-community.cachix.org https://thiagokokada-nix-configs.cachix.org&#39; (y/N)? y
237 </span></span></span><span class="line"><span class="cl"><span class="go">do you want to permanently mark this value as trusted (y/N)? y
238 </span></span></span></code></pre><p>And realise that you did/didn't want to mark this value as trusted? But where
239 is this stored? Well, I had to look at the Nix source code to find the answer,
240 since I can't find this anywhere in the web or in ChatGPT (but I am sure that
241 now that I posted this it will eventually be searchable), and the answer can be
242 found
243 <a href="https://github.com/NixOS/nix/blob/c116030605bf7fecd232d0ff3b6fe066f23e4620/src/libflake/flake/config.cc#L13-L16">here</a>:</p>
244 <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">Path</span> <span class="nf">trustedListPath</span><span class="p">()</span>
245 </span></span><span class="line"><span class="cl"><span class="p">{</span>
246 </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">getDataDir</span><span class="p">()</span> <span class="o">+</span> <span class="s">&#34;/trusted-settings.json&#34;</span><span class="p">;</span>
247 </span></span><span class="line"><span class="cl"><span class="p">}</span>
248 </span></span></code></pre><p>Where is <code>getDataDir()</code> though? I found the answer
249 <a href="https://github.com/NixOS/nix/blob/c116030605bf7fecd232d0ff3b6fe066f23e4620/src/libutil/users.cc#L52-L65">here</a>:</p>
250 <pre class="chroma"><code><span class="line"><span class="cl"><span class="n">Path</span> <span class="nf">getDataDir</span><span class="p">()</span>
251 </span></span><span class="line"><span class="cl"><span class="p">{</span>
252 </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">dir</span> <span class="o">=</span> <span class="n">getEnv</span><span class="p">(</span><span class="s">&#34;NIX_DATA_HOME&#34;</span><span class="p">);</span>
253 </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">dir</span><span class="p">)</span> <span class="p">{</span>
254 </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="n">dir</span><span class="p">;</span>
255 </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
256 </span></span><span class="line"><span class="cl"> <span class="k">auto</span> <span class="n">xdgDir</span> <span class="o">=</span> <span class="n">getEnv</span><span class="p">(</span><span class="s">&#34;XDG_DATA_HOME&#34;</span><span class="p">);</span>
257 </span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">(</span><span class="n">xdgDir</span><span class="p">)</span> <span class="p">{</span>
258 </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">*</span><span class="n">xdgDir</span> <span class="o">+</span> <span class="s">&#34;/nix&#34;</span><span class="p">;</span>
259 </span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
260 </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="n">getHome</span><span class="p">()</span> <span class="o">+</span> <span class="s">&#34;/.local/share/nix&#34;</span><span class="p">;</span>
261 </span></span><span class="line"><span class="cl"> <span class="p">}</span>
262 </span></span><span class="line"><span class="cl"> <span class="p">}</span>
263 </span></span><span class="line"><span class="cl"><span class="p">}</span>
264 </span></span></code></pre><p>So we solved the mystery:</p>
265 <ul>
266 <li>If <code>NIX_DATA_HOME</code> is set, the file will be in
267 <code>$NIX_DATA_HOME/trusted-settings.json</code></li>
268 <li>If <code>XDG_DATA_HOME</code> is set, the file will be in
269 <code>$XDG_DATA_HOME/nix/trusted-settings.json</code></li>
270 <li>Otherwise Nix will fallback to <code>$HOME/.local/share/nix/trusted-settings.json</code></li>
271 </ul>
272 <p>By the way, if you don't know why you got this prompt, if <code>flake.nix</code> has a
273 <code>nixConfig</code> attribute inside <code>outputs</code> <strong>and</strong> this <code>nixConfig</code> is an unsafe
274 attribute (like <code>extra-substituters</code>) you will get this prompt, unless you set
275 <a href="https://nix.dev/manual/nix/2.23/command-ref/conf-file#conf-accept-flake-config"><code>accept-flake-config = true</code></a>
276 in your Nix configuration (please <strong>do not do this</strong>, it is dangerous because
277 it may allow running possible unsafe options without asking you first).</p>
278 <p>You can inspect the JSON file or delete it and Nix will prompt you again the
279 next time you run a <code>nix</code> command. And yes, saving this preference is
280 controversial considering this is Nix, but having the power to set <code>nixConfig</code>
281 attributes is really powerful, and with great powers comes great
282 responsibilities.</p></summary></entry><entry><title>My coding experience with LLM</title><link href="https://kokada.dev/blog/my-coding-experience-with-llm/" rel="alternate"/><published>2024-09-02T00:00:00+00:00</published><id>https://kokada.dev/blog/my-coding-experience-with-llm/</id><summary type="html"><p>I don't want to talk about AI. AI fatigue is real, with (garbage) AI content
283 everywhere you go. However, I like to talk about tools, and AIs, or more
284 specifically, <a href="https://en.wikipedia.org/wiki/Large_language_model">Large Language
285 Models</a> (LLM), can be
286 really useful tools. Maybe someday we will have our AI overlords dominating
287 everything (including my job), but right now I don't feel particularly
288 threatened given my experience with AIs.</p>
289 <p>Given that <a href="https://mashable.com/article/google-search-low-quality-research">Google Search is getting
290 worse</a>, I am
291 slowly relying in LLMs more and more. This is the focus of this post, basically
292 <a href="https://www.rottentomatoes.com/m/the_good_the_bad_and_the_ugly">the good, the bad and the
293 ugly</a> (by the
294 way, never saw this film) of LLMs, especially focused for solving coding
295 problems. I will take a look at the my chat history in
296 <a href="https://chatgpt.com/">ChatGPT</a> and see where it was a good tool, where it
297 shined, and where it just completely wasted my time.</p>
298 <p>A disclaimer, I never bothered to buy a ChatGPT subscription because I don't
299 feel that the tool is useful enough (at least yet), so most of the examples
300 here are either with <a href="https://openai.com/index/chatgpt/">GPT-3.5</a> or <a href="https://openai.com/index/gpt-4o-mini-advancing-cost-efficient-intelligence/">GPT-4o
301 mini</a>,
302 since those where the models offered by free in ChatGPT.</p>
303 <p>An addendum, I have some experience with <a href="https://github.com/features/copilot">GitHub
304 Copilot</a> (another LLM, but this one runs
305 inside your IDE or favorite text editor) in <code>$PREVIOUS_JOB</code>. But my experience
306 with it is even more underwhelming than what I am going to describe in this
307 post: it was basically really good for generating fixture for tests and between
308 ok-ish to plain bad for everything else. Yes, that's would fit in a tweet (or
309 xeet?).</p>
310 <h2>The good</h2>
311 <p><a href="https://chatgpt.com/share/c58a1432-2761-4954-acac-cade29942c34">Chat history</a>.</p>
312 <blockquote>
313 <p>Is there any way that I can pass a context for a net.conn to cancel its
314 Read()?</p>
315 </blockquote>
316 <p>This is one of the good cases where ChatGPT felt to me that I was talking with
317 an expert in Go. To start, I didn't even mention in my initial prompt that I
318 was talking about Go, but it correctly inferred so. Also, the solution overview
319 was great:</p>
320 <blockquote>
321 <ol>
322 <li><strong>Use a separate goroutine</strong> to handle the context cancellation.</li>
323 <li><strong>Set a deadline</strong> on the connection using <code>SetReadDeadline()</code> to unblock
324 the <code>Read()</code> when the context is canceled.</li>
325 </ol>
326 </blockquote>
327 <p>Before asking ChatGPT about this issue I had already coded my own naive
328 solution that was something similar to this:</p>
329 <pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">foo</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">)</span> <span class="p">{</span>
330 </span></span><span class="line"><span class="cl"> <span class="c1">// ...
331 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kd">struct</span><span class="p">{})</span>
332 </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">n</span> <span class="kt">int</span>
333 </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">err</span> <span class="kt">error</span>
334 </span></span><span class="line"><span class="cl">
335 </span></span><span class="line"><span class="cl"> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
336 </span></span><span class="line"><span class="cl"> <span class="nx">n</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">conn</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">buf</span><span class="p">)</span>
337 </span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">&lt;-</span><span class="kd">struct</span><span class="p">{}{}</span>
338 </span></span><span class="line"><span class="cl"> <span class="p">}()</span>
339 </span></span><span class="line"><span class="cl">
340 </span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
341 </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span>
342 </span></span><span class="line"><span class="cl"> <span class="nx">conn</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
343 </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</span><span class="p">()</span>
344 </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o">&lt;-</span><span class="nx">done</span><span class="p">:</span>
345 </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">n</span><span class="p">,</span> <span class="nx">err</span>
346 </span></span><span class="line"><span class="cl"> <span class="p">}</span>
347 </span></span><span class="line"><span class="cl"> <span class="c1">// ...
348 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
349 </span></span></code></pre><p>Compared this solution with the one from ChatGPT:</p>
350 <pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">readWithContext</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">conn</span> <span class="nx">net</span><span class="p">.</span><span class="nx">Conn</span><span class="p">,</span> <span class="nx">buf</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
351 </span></span><span class="line"><span class="cl"> <span class="nx">done</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="kd">struct</span><span class="p">{})</span>
352 </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">n</span> <span class="kt">int</span>
353 </span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">err</span> <span class="kt">error</span>
354 </span></span><span class="line"><span class="cl">
355 </span></span><span class="line"><span class="cl"> <span class="c1">// Start a goroutine to perform the read
356 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">go</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
357 </span></span><span class="line"><span class="cl"> <span class="nx">n</span><span class="p">,</span> <span class="nx">err</span> <span class="p">=</span> <span class="nx">conn</span><span class="p">.</span><span class="nf">Read</span><span class="p">(</span><span class="nx">buf</span><span class="p">)</span>
358 </span></span><span class="line"><span class="cl"> <span class="nb">close</span><span class="p">(</span><span class="nx">done</span><span class="p">)</span>
359 </span></span><span class="line"><span class="cl"> <span class="p">}()</span>
360 </span></span><span class="line"><span class="cl">
361 </span></span><span class="line"><span class="cl"> <span class="k">select</span> <span class="p">{</span>
362 </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span> <span class="c1">// Context was canceled or timed out
363 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// Set a short deadline to unblock the Read()
364 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">conn</span><span class="p">.</span><span class="nf">SetReadDeadline</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">())</span>
365 </span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> <span class="c1">// Wait for the read to finish
366 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</span><span class="p">()</span>
367 </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o">&lt;-</span><span class="nx">done</span><span class="p">:</span> <span class="c1">// Read finished successfully
368 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">n</span><span class="p">,</span> <span class="nx">err</span>
369 </span></span><span class="line"><span class="cl"> <span class="p">}</span>
370 </span></span><span class="line"><span class="cl"><span class="p">}</span>
371 </span></span></code></pre><p>The ChatGPT version is much better because:</p>
372 <ol>
373 <li>It doesn't close the connection socket allowing it to be reused</li>
374 <li>It waits until the goroutine is finished to avoid leaks</li>
375 <li>It closes the channel after it finishes</li>
376 </ol>
377 <p>Now, I don't claim to be a Go expert (far from it), but this was one of those
378 cases where I felt that I was being guided by someone that is coding in Go for
379 years.</p>
380 <p>But it didn't end there, I asked:</p>
381 <blockquote>
382 <p>If I want to reuse the connection, do I need to reset the SetReadDeadline?</p>
383 </blockquote>
384 <p>Since in this particular case I wanted the underlying connection to be reused,
385 and ChatGPT correctly amended the code:</p>
386 <pre class="chroma"><code><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">readWithContext</span><span class="p">(</span><span class="nx">ctx</span> <span class="nx">context</span><span class="p">.</span><span class="nx">Context</span><span class="p">,</span> <span class="nx">conn</span> <span class="nx">net</span><span class="p">.</span><span class="nx">Conn</span><span class="p">,</span> <span class="nx">buf</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="kt">int</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
387 </span></span><span class="line"><span class="cl"> <span class="c1">// Same as before
388 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">select</span> <span class="p">{</span>
389 </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o">&lt;-</span><span class="nx">ctx</span><span class="p">.</span><span class="nf">Done</span><span class="p">():</span> <span class="c1">// Context was canceled or timed out
390 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// Set a short deadline to unblock the Read()
391 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">conn</span><span class="p">.</span><span class="nf">SetReadDeadline</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nf">Now</span><span class="p">())</span>
392 </span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">done</span> <span class="c1">// Wait for the read to finish
393 </span></span></span><span class="line"><span class="cl"><span class="c1"></span>
394 </span></span><span class="line"><span class="cl"> <span class="c1">// Reset the deadline so the connection can be reused
395 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">conn</span><span class="p">.</span><span class="nf">SetReadDeadline</span><span class="p">(</span><span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="p">{})</span>
396 </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">ctx</span><span class="p">.</span><span class="nf">Err</span><span class="p">()</span>
397 </span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o">&lt;-</span><span class="nx">done</span><span class="p">:</span> <span class="c1">// Read finished successfully
398 </span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">return</span> <span class="nx">n</span><span class="p">,</span> <span class="nx">err</span>
399 </span></span><span class="line"><span class="cl"> <span class="p">}</span>
400 </span></span><span class="line"><span class="cl"><span class="p">}</span>
401 </span></span></code></pre><p>Also the explanation on why this happens was good:</p>
402 <blockquote>
403 <p>When you set a read deadline with <code>conn.SetReadDeadline(time.Now())</code> to unblock
404 the <code>Read()</code> call, this deadline remains in effect for future <code>Read()</code>
405 operations. This means that if you attempt another read operation after this,
406 it could immediately fail with a timeout error if the deadline is still in
407 effect.
408 To reuse the connection, you'll need to remove the deadline or set a new one.
409 You can do this by calling <code>conn.SetReadDeadline(time.Time{})</code>, which clears
410 any existing deadline.</p>
411 </blockquote>
412 <p>I had one last question that I kind already knew the answer:</p>
413 <blockquote>
414 <p>Do we need to defer the clearing?</p>
415 </blockquote>
416 <p>And ChatGPT correctly returned:</p>
417 <blockquote>
418 <p>Whether you need to defer the clearing of the read deadline depends on how
419 your code is structured. If you want to ensure that the read deadline is
420 always cleared after a context cancellation, even if the code panics or exits
421 early, then deferring the <code>conn.SetReadDeadline(time.Time{})</code> call is a good
422 idea.</p>
423 </blockquote>
424 <p>This whole interaction was one of the best ones I ever had in ChatGPT, one that
425 it gave me an actual functional code with good explanation on what was
426 happening. But still, it didn't talked at all about the gotcha of setting the
427 read deadline and reusing the underlying connection, needing another prompt to
428 give me this information.</p>
429 <p>Also, there is one issue in ChatGPT solution though: we are discarding the
430 values of the buffer once the context is done by returning <code>n = 0</code> instead of
431 the number of bytes read. In this particular case this is what I wanted, but
432 this is a possible issue that ChatGPT didn't talk.</p>
433 <p>For comparison,
434 <a href="https://stackoverflow.com/questions/61637359/how-to-use-context-context-with-tcp-connection-read">this</a>
435 is the best that I got searching in both DuckDuckGo and Google, that is where I
436 kind based my first solution on. I still not sure where ChatGPT could give me
437 such a good answer for this question, because I just couldn't find anything
438 remotely similar during my searches.</p>
439 <h2>The amazing</h2>
440 <p><a href="https://chatgpt.com/share/0c9a148f-4cfd-4b4e-aa84-7486e185125b">Chat history</a>.</p>
441 <blockquote>
442 <p>I want to write an extension to <a href="https://github.com/yuin/goldmark/">https://github.com/yuin/goldmark/</a> that allows
443 me to rewrite <code>[link](../2024-07-30/post.md)</code> to
444 <code>[link](https://kokada.capivaras.dev/slug)</code></p>
445 </blockquote>
446 <p>Here is the generated code:</p>
447 <pre class="chroma"><code><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">linkrewriter</span>
448 </span></span><span class="line"><span class="cl">
449 </span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
450 </span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span>
451 </span></span><span class="line"><span class="cl"> <span class="s">&#34;path&#34;</span>
452 </span></span><span class="line"><span class="cl"> <span class="s">&#34;strings&#34;</span>
453 </span></span><span class="line"><span class="cl">
454 </span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/yuin/goldmark&#34;</span>
455 </span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/yuin/goldmark/ast&#34;</span>
456 </span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/yuin/goldmark/parser&#34;</span>
457 </span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/yuin/goldmark/renderer&#34;</span>
458 </span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/yuin/goldmark/util&#34;</span>
459 </span></span><span class="line"><span class="cl"><span class="p">)</span>
460 </span></span><span class="line"><span class="cl">
461 </span></span><span class="line"><span class="cl"><span class="c1">// LinkRewriter is the main struct for your extension
462 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">LinkRewriter</span> <span class="kd">struct</span><span class="p">{}</span>
463 </span></span><span class="line"><span class="cl">
464 </span></span><span class="line"><span class="cl"><span class="c1">// NewLinkRewriter returns a new instance of LinkRewriter
465 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">NewLinkRewriter</span><span class="p">()</span> <span class="o">*</span><span class="nx">LinkRewriter</span> <span class="p">{</span>
466 </span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">&amp;</span><span class="nx">LinkRewriter</span><span class="p">{}</span>
467 </span></span><span class="line"><span class="cl"><span class="p">}</span>
468 </span></span><span class="line"><span class="cl">
469 </span></span><span class="line"><span class="cl"><span class="c1">// Extend will be called by Goldmark to add your extension
470 </span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="o">*</span><span class="nx">LinkRewriter</span><span class="p">)</span> <span class="nf">Extend</span><span class="p">(</span><span class="nx">m</span> <span class="nx">goldmark</span><span class="p">.</span><span class="nx">Markdown</span><span class="p">)</span> <span class="p">{</span>
471 </span></span><span class="line"><span class="cl"> <span class="nx">m</span><span class="p">.</span><span class="nf">Parser</span><span class="p">().</span><span class="nf">AddOptions</span><span class="p">(</span><span class="nx">parser</span><span class="p">.</span><span class="nf">WithASTTransformers</span><span class="p">(</span><span class="nx">util</span><span class="p">.</span><span class="nf">Prioritized</span><span class="p">(</span><span class="nx">e</span><span class="p">,</span> <span class="mi">0</span><span class="p">)))</span>
472 </span></span><span class="line"><span class="cl"><span class="p">}</span>
473 </span></span><span class="line"><span class="cl">
474 </span></span><span class="line"><span class="cl"><span class="c1">// Transform is the method that modifies the AST
codemadness.org:70 /git/sfeed_tests/file/input/sfeed/realworld/kokada.dev.atom.xml.gph:485: line too long