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