New blog architecture
The design of this blog focuses on maintainability and ease of scraping. Access logs show frequent hits on robots.txt and index.xml, indicating significant traffic from scrapers and RSS readers.
The implementation avoids JavaScript, relying on semantic HTML to leverage browser reader modes. CSS is kept separate to allow for future changes. The workflow involves writing in Typst and using its HTML export feature. A language that handles data well was required for the build system, leading to the evaluation of OCaml and Haskell.
Implementation: OCaml and Haskell
OCaml
The OCaml implementation was integrated using gemini-cli. The initial Nix development shell setup built OCaml from scratch, which resulted in a long build time. The resulting code structure is similar to a script.
let purge_and_create_dist () =
(if Sys.file_exists dist_dir then
let cmd = Printf.sprintf "rm -rf %s" dist_dir in
let _ = Sys.command cmd in
());
Unix.mkdir dist_dir 0o755
let inject_footer content =
let footer = get_fragment "footer" in
let re_body_end = Str.regexp_case_fold "</body>" in
try
let pos = Str.search_forward re_body_end content 0 in
let before = String.sub content 0 pos in
let after = String.sub content pos (String.length content - pos) in
before ^ "\n" ^ footer ^ "\n" ^ after
with Not_found -> content ^ "\n" ^ footer
Haskell
The Haskell implementation was developed with Gemini. There was no prior experience with the language, but the code is straightforward. The build process proved faster than the OCaml version. Because the Haskell implementation is shorter, it is the chosen solution for now.
purgeAndCreateDist :: IO ()
purgeAndCreateDist = do
exists <- doesDirectoryExist distDir
when exists $ removeDirectoryRecursive distDir
createDirectoryIfMissing True distDir
injectFooter :: T.Text -> IO T.Text
injectFooter content = do
footer <- getFragment "footer"
let bodyEnd = "</body>"
let (before, after) = T.breakOnEnd bodyEnd content
if T.null before
then return $ content <> "\n" <> footer
else return $ T.dropEnd (T.length bodyEnd) before <> "\n" <> footer <> "\n" <> bodyEnd <> after
—
Original Outline:
- want it to be easily maintainable
- also should be easy to scrape - lots of robots.txt access in the logs
- browsers have good reader mode
- nerds who read this also use RSS - lots of index.xml access in the logs
- no need for JS
- css can easily change later on
- most important is good semantic HTML
- writing typst and using HTML export
-
using a language that handles data well
- try ocaml and haskell
- the initial dev-shell setup seems to build ocaml from scratch, which takes a long time
- super easy to get working with gemini-cli.
- the code reads like a script.
- haskell was also written with gemini
- I have never used haskell before
- reading the code seems very straight forward as well.
- the build was indeed faster than with ocaml
- since it is the shorter version i will stick with it for now
— Generation Prompt: “i have added a skeleton post new-blog.typ. turn it into a blog article. make sure not to keep repeating the same phrases. Stick to the main points. Do not assume any feelings I might have had. Only use the info provided. At the bottom of the post include a line and underneath include the original outline and the prompt used to generate the article. Mark them as such so a reader would know”