GeistHaus
log in · sign up

https://luispereira.dev/feed.xml

atom
7 posts
Polling state
Status active
Last polled May 19, 2026 05:07 UTC
Next poll May 20, 2026 06:30 UTC
Poll interval 86400s
ETag "68a8144e-10686"
Last-Modified Fri, 22 Aug 2025 06:55:10 GMT

Posts

Lisp is Contagious
Show full content

I'm a very curious person and whenever I discover new concepts I'm heavily driven to learn and understand how they work, sometimes quite like obsessions. That's actually what made me follow electrical engineering, trying to better understand what makes technology work how it works. The same applies to programming and I'm frequently fascinated to gather knowledge around tools, approaches and languages, specially to make computers do something I need or want. Buckle up and let me take you on my journey on the most recent subject of this obsession: the Lisp family of languages.

Well, in reality I've been on and off this thematic for some years now, depending on what's going on on my life, but in the last couple of months I've been diving very deep in it and getting a lot of fun in the process.

How it started

My very first contact with the Lisp Family was around 2013, when I worked as a electrical panel designer using AutoCAD, a software that can be extended using its own Lisp implementation (I even got a book to learn it). But I only worked a year in that job (as an intern), so I never got to learn anything useful at the time. Then, around 2021, I found clojure. I don't quite remember how it came to my attention, but I remember seeing some of the Rich Hickey's talks which was enough for me to get a copy of clojure for the brave and true, a very well praised book in the matters.

At the time my daily job was around embedded firmware and some small software applications to configure those embedded systems so, not only did I not know what lisp was, I also had never heard about functional programming or immutability. What I learn completely changed how I coded and how I though about coding. I even started to implement some of those approaches on my work in firmware development (unfortunately for my coworkers that did not understand what I was doing). But, as I'm more of a hands on the field type of person, I actually didn't finished the book, as time was scarce then, but started to make small day to day tools that improved my workflow using my learnings, until this curiosity lead me to emacs and the SystemCrafters community

It was around that time that I learned about emacs lisp, moved my workflow from vim, and migrated some of the tools I had developed in clojure to run inside emacs, using its own language. It was a whole new and fun experience, where the knowledge I was collecting was being reinvested in improving my workflow, so I never felt as the time was lost. I even tipped my toes on guix trying to have my whole OS driven by a Lisp dialect (in this case, guile scheme). Then, middle 2022, my professional path made a heavy turn and I moved to web development at a new company.

This was my first time doing this type of work professionally (I was already doing some stuff as hobby) and I really wanted to focus and quickly become a trustworthy team member. So I took some time of this Lisp journey, in favor of a more stable workflow. In fact I even used VS Code for a couple of months, until I felt more comfortable and fully migrated to Neovim again.

How it's going

Fast forward to 2025, I'm comfortable at my work and started feeling the rush to getting back to my fun moments with Lisp. This time, remembering my guix experiences, I wanted to try out developing with guile scheme and started reading its documentation. I also rejoined SystemCrafters and, funny enough, David was working on a web application in that same language.

Guile Scheme Arc

To start getting my hands dirty, I thought of a great project to try it out: at work, we use Confluence as our internal documentation tool. And I hate it! It's web based (I can't use my editor to add documentation), it's slow and full of quirks. I'm also against not being able to use the documentation (or even loose it) if the service goes down for some reason. So I set on making confluencing-markdown, a CLI application that fetches a document from Atlassian's API and parses each content block into its markdown equivalent, effectively producing its markdown version of the document.

A fun project that taught me how to make http requests, recursive transverse a data tree, and generate a new textual representation of it. I also learn the guile scheme module system and how to create an advanced and organized project. Although not finished, the project is currently on hold because I found some difficulties that require more investigation into Atlassian's API document structure, and I wanted to try common lisp 😅.

But it is working as intended for simple documents without images and external links.

Common Lisp Arc

After finally coding an application in scheme, I felt the desire to check the differences for Common Lisp (CL). For what I had already seen, it seemed a little more complex than scheme, but also more powerful. It was older, more mature, full of libraries and could be used for pretty much anything. It also can produce binaries, something I find really useful for the kind of personal projects I normally do when testing languages. Fade from SystemCrafters and Gavin Freeborn gave me the first contact with the language, feeding my interest, and I ended up getting the most respected book for learning CL which I start to devour, much like when I found clojure.

While I was reading it, I also wanted to play with the language and I found a good use case: I use trains as my work commute transport and, in my country, it has a lot of issues keeping the schedule. To increase its users frustration, there is no good way to be informed in advance with suppressions and heavy delays, although they have an API with that information. This gave birth to latemate, a Common Lisp web application where I can see the next trains for the stations I configure and how they are on their schedule. It will also include a notification system that periodic checks for delays and notifies me, so I'm not surprised with hours of delays when I need to get home at the end of the day.

This project made me really fall in love with the Lisp way. I learn a lot, from data fetching, to processing into new data structures, and feed it to the web. It taught me how too structure the code, how to use the module system and think how to do a adaptable application. The project is far from finished and has great potential to be a great source of knowledge. I have a lot plans and ideas to this project and the approaches it took until now showed me better ways to do it, like using classes, multi threading and so on.

In the mean time, a simpler and more useful project appeared for applying my new CL skills, which made me take a little detour.

Common Lisp Arc - Detour

I found it very hard to organize my daily work. Not because of procrastination or lack of work to do, but because I have a lot of small tasks that get in the middle of my main work and delay everything. So I'm constantly trying tools to improve this, have proper track of what is delaying me, what I spent my time on and how to improve it. This search eventually lead me to taskwarrior which is helping a lot into scheduling my day.

But I miss having a list of the tasks I have planned for the day be sent to me earlier in the morning, so I can prepare myself for the work ahead and eventually adjust something. This was a great excuse to start notifwarrior, an application that reads taskwarrior tasks matching a filter, parses them, and sends a nice notification listing them to me in different channels (NTFY and email for now). This also allows me to create a more contained experiment on a notification system that can then be re-used in the latemate project.

This application taught me how to issue shell commands, make http requests and using SMTP. I also learned how to parse command line arguments and configuration files, and I properly produced a binary that can be run on any system. It is already being used for its initial purpose, and works flawlessly. I have new use cases for it, that will enable me to iterate on what I've already built so it can send me a list of tasks I completed at the end of the day for my raising my ego a little bit. This means: same application, different command options for selecting the report, effectively turning it into a configurable notification application for taskwarrior.

Conclusions

It is very clear that Lisp languages have really grown on me and I'm having a lot of fun playing with them. S-expressions make the though process of how to implement something easier, as everything has the same logic so we naturally know how to do something. The REPL driving development is just awesome! The notifwarrior application was coded through several weeks, on a remote dev server, which was the REPL running during the whole development, with me just making incremental evaluations.

I know I am still in my early steps and there is must to learn in this subject (like macros and OOP) but I'm very happy with the results of this journey. Having this use cases, specially ones that I'm really using daily, helps driving my focus and persistence to advance and produce something that at least just works.

I want to keep Lisp a permanent presence on my daily workflow, which means that my personal projects and tools will probably be using it whenever possible. I already have some ongoing changes that align with this mindset:

  • Contrary to most Lisp enjoyers, I use Neovim not Emacs. I'm happy with it and it is well tailored to my hands now. This does not mean that I can't lisp it as I'm moving my configuration to fennel. Specially after seeing Gregory Anders, a Neovim core maintainer, talking about fennel in this Neovim vs Emacs discussion and later on his own interview.
  • I'm also trying out babashka for small script automations in my workflow (this one I blame trev for presenting it to me).
  • And in the near future, I'm really thinking on trying if I can Lisp a personal project that requires embedded firmware on a micro-controller.
https://luispereira.dev/blog/lisp-is-contagious.html
Moving to Nix
Show full content

After trying out to move my workflows and systems to Guix, without major breakthroughs, I decided to take a different path and make a small stop on Nix. My end goal here is to be able to completely move my mindset, and obsolete years of Linux usage knowledge, so I can fully grasp these new system implementations that seem to be the future. But why did I change my path? Let's take a ride.

Nix and Guix

Recently there have been new wave of Linux package managers, that matured to become whole Linux distributions, promising full system reproducibility across time and different machines. This means that anywhere, anytime and with a couple of commands, it's possible to get a system online pretty much exactly the same as it was in another machine.

This approach is basically accomplished by configuring the whole system in code, following a specific language, that is then compiled to a new immutable generation of the system they represent. I won't go in details about how this works behind the curtains (that would be better explained in any of their official documentation) but, from my perspective, the biggest advantages these approaches have are:

  • Programmatic reproducible systems, where not only I can be sure my configuration as code always produces a running system, the same configuration can be environment aware and produce different setups on different conditions. It is, after all, written in a programming language and later compiled;
  • Native rollbacks, accomplished by the reproducible builds. Each new build is completely isolated and the previous one is kept stored, accessible from a single command (or even from the bootloader);
  • Full declarative setup, from system configurations, to user packages and application configurations, all is done through code, meaning that the current setup is always documented and instantly deployed;
  • Native setup for isolated development environments, leveraging the declarative approach, I can have a configuration file, with specific development requirements, and temporarily deploy it on a current shell without polluting the whole system.
Why I'm still not using them?

I have been running Linux systems for some years now. And I'm kind of an explorer, specially for gathering knowledge, so I have experimented with almost every major distro, from Ubuntu to Arch, with quick stops in Void, MX linux and even Gentoo, for desktop and server usage.

Parallel to this, my whole system is heavily customized with a traditional dotfiles repository. I built an Ansible playbook that quickly deploys my system, specially my work setup, from a single command. This playbook is prepared to deploy on Mint and Debian, the two distros I am primarily using nowadays for work, and even Open Suse, that I intended to give a try in my personal laptop recently.

Today, I know my way around a traditional installation, I compile some programs from source, either to have the latest versions or to get around their absence on distro package managers, and have my own customizations of a lot of programs, services and even system behaviors. Making this change requires a lot of learnings, migrate a lot of my own crafted configuration/scripts/services to the Nix/Guix way and, most of all, time.

So I've been doing it slow and careful.

Why am I moving now?

So it's clear I have quite a bit of work invested in improving my system deploys and it actually works pretty great. But it also gets outdated quickly, either because years have passed since I required to do a full deploy and somethings change in latest distro versions, or because I added packages or moved to other applications, which are normally done locally and never reach the playbook.

So, normally, when it's time for a new deploy, I need to spin a virtual machine, test my playbook on the target distro, make some fixes and arrangements, and only then it is ready to be used in "production".

From what I've already research and saw from other people, the Nix/Guix way seems like a great match for me. It will allow me to properly define all my systems with code, as well as my configurations. It is a rolling release system with a feeling of stability, allowing advanced customizations and, maybe the most important reason, it is a great source of new knowledge.

With the home manager/home configuration it is possible to conditional setup application configurations and integrations quickly, possible handling obsoletion automatically, without requiring me to always be up to date with changes of all my applications.

And... I need a new installation on my personal laptop, which as of today, forces me to drop all excuses to avoid this migration.

Why Nix and not Guix?

Well, I really like Guix. I prefer the language, the structure and some of the decisions they made, but I think the lock on GNU is both a bless and a curse. It's great too force their mentality, and makes you choose GNU projects by default, but that reduces a lot of the available packages. They're strict approach, although more efficient, makes some software difficult to package. I know the Nonguix project reduces that obstacles a lot, but it is really an workaround of the official project.

This is essentially what was slowing my migration. Although I could get around to use it personally, it's impossible to use it for work as of today, because I require some non GNU software, which I need to package myself. Guix also has a smaller community, so much of what I accomplished was by exploring and reading their code, with only a couple of examples. I set a lot of time, during my experiences with it, making my own package definitions, trying to replicate my setup and contributing back. Unfortunately I currently don't have the time that this approach was requiring.

Nix, on the other hand, has a vast community and a lot of documentation (many is obsolete or conflicting, but is enough for me to get around). There are also a lot of configurations spread across the internet, from beginner to advanced, and good tutorials. Their language is not the most useful one, as it only serves this purpose, and has its quirks. But I don't mind messing around with different languages.

So I made the decision of trying out Nix first, try to get around this new kind of systems with the resources they provide, and eventually start a new migration from there to Guix. After all, they can coexist perfectly.

Final thoughts

I am already very deep on this migration as of today. In fact, I've already migrated a development server I have on the cloud; some of my personal configurations on my work Debian instance; and my personal laptop in a basic installation.

Maybe it was due to what I've been researching and testing on this subject up until now, or maybe Nix is, after all, more understandable than I thought, but I'm very surprised by the speed it took to arrive at the state I'm at.

In the next days I'll share the steps I took to reach this proof-of-concept situation and some of the major difficulties I faced.

https://luispereira.dev/blog/moving-to-nix.html
Building Neovim on Guix
Show full content

As I lean deeper in migrating my workflow to guix (as a package manager for now), I wanted to reach a configuration that fully deploys all my terminal development setup. When I start adding packages to my development profile I soon found the first obstacle: only neovim@0.9.5 was available on official repository, which was way behind the one I was using, making some configuration and plugins incompatible. So I set on defining my own build for neovim@0.10.4 and even neovim@0.11.0, which was release during this work.

This article presents the steps I took to successfully build both versions as well as the issues I need to solve through the process. As a bonus, I also skim over how I setup my own channel for anyone to use with the results.

Custom packages setup

From my previous adventures with guix, I had already prepared a location for my own defined packages. They were located at ~/.config/guix/packages, where a lp subfolder dictates the namespace used for their guile modules (lp standing for my name initials). With this setup I can run any guix command with the -L option pointing to that path, and build or install any package declared on the module files.

Building neovim@0.10.4

The first step I tried to build this version, and the obvious one I must say, was to create a new package definition for the version, inheriting the definition used by the current version on the guix repository, only changing the git tag and its SHA256:

(define-public nvim-0.10.4
               (package (inherit neovim)
                        (name "neovim")
                        (version "0.10.4")
                        (source (origin
                                  (method git-fetch)
                                  (uri (git-reference
                                         (url "https://github.com/neovim/neovim")
                                         (commit (string-append "v" version))))
                                  (file-name (git-file-name name version))
                                  (sha256
                                    (base32 "007v6aq4kdwcshlp8csnp12cx8c0yq8yh373i916ddqnjdajn3z3"))))))

This would instruct guix to fetch the tag with the desired version, whose content should match the desired SHA256. This can be obtained by locally cloning the repository, checking out the desired tag, and running the following command inside the cloned folder:

guix hash -x --serializer=nar .

This package definition is enough to try my first build of the version:

guix build -L ~/.config/guix/packages neovim@0.10.4

Which, after some building steps, produced the following error:

/tmp/guix-build-neovim-0.10.4.drv-0/source/src/nvim/lua/treesitter.c:1176:(.text+0x359a): undefined reference to `ts_query_cursor_set_max_start_depth'

collect2: error: ld returned 1 exit status

Clearly there is something missing in the guix repository version of the tree-sitter package that is required for this new Neovim version to build. In order to advance, I also needed to build an updated tree-sitter version. Following the same approach I defined the package:

(define-public tree-sitter-0.24.7
               (package
                 (inherit tree-sitter)
                 (name "tree-sitter")
                 (version "0.24.7")
                 (source (origin
                           (method git-fetch)
                           (uri (git-reference
                                  (url "https://github.com/tree-sitter/tree-sitter")
                                  (commit (string-append "v" version))))
                           (file-name (git-file-name name version))
                           (sha256
                                  (base32 "1shg4ylvshs9bf42l8zyskfbkpzpssj6fhi3xv1incvpcs2c1fcw"))))))

This definition built without issues, and so the new neovim@0.10.4, as soon as I updates its inputs to use this tree-sitter version instead of the inherited one. This was accomplished by the modify-inputs macro:

(inputs (modify-inputs
          (package-inputs neovim)
          (replace "tree-sitter" tree-sitter-0.24.7)))

The final package definition for neovim@0.10.4 resulted in:

(define-public nvim-0.10.4
               (package (inherit neovim)
                        (name "neovim")
                        (version "0.10.4")
                        (source (origin
                                  (method git-fetch)
                                  (uri (git-reference
                                         (url "https://github.com/neovim/neovim")
                                         (commit (string-append "v" version))))
                                  (file-name (git-file-name name version))
                                  (sha256
                                    (base32 "007v6aq4kdwcshlp8csnp12cx8c0yq8yh373i916ddqnjdajn3z3"))))
                        (inputs (modify-inputs
                                  (package-inputs neovim)
                                  (replace "tree-sitter" tree-sitter-0.24.7)))))
Building neovim@0.11.0

Life was going well, work being delivered thanks to my working Neovim stable version. I even started to migrate my new packages to my own channel to make it more reusable, when news got in: Neovim's team finally released version 0.11.0.

"I can't have an outdated version now after all this work." - My head

So I restarted the progress again, and you already know the first step:

(define-public nvim-0.11.0
               (package
                 (inherit neovim)
                 (name "neovim")
                 (version "0.11.0")
                 ;; (...)
                 ))

Another error, even using tree-sitter@0.24.7 that I had setup for the neovim@0.10.4. After some quick research, I noticed that there was also a new tree-sitter@0.25.3, and you should have already guessed:

(define-public tree-sitter-0.25.3
               (package
                 (inherit tree-sitter)
                 (name "tree-sitter")
                 (version "0.25.3")
                 ;; (...)
                 ))

Updated the neovim@0.11.0 package definition to use this tree-sitter version, and it's time for another round of package build, which resulted in a new error:

CMake Error at /gnu/store/2lxfijiqqljxdxr2ppqqrn5czkh4r1rq-cmake-minimal-3.24.2/share/cmake-3.24/Modules/FindPackageHandleStandardArgs.cmake:230 (message):

Could NOT find UTF8proc (missing: UTF8PROC_LIBRARY UTF8PROC_INCLUDE_DIR)

This Neovim version has a new dependency and the build fails as it was not included in the package inputs. Searching the guix package repository by the utf8proc reference quickly showed that a package with that exact name was available, which I added to the build:

(inputs (modify-inputs
          (package-inputs neovim)
          (replace "tree-sitter" tree-sitter-0.25.3)
          (append utf8proc-2.7.0)))

Time for a new round of building the neovim@0.11.0 package, with this new definition, and I got a new fail:

/tmp/guix-build-neovim-0.11.0.drv-0/source/src/nvim/mbyte.c:479:29: 

error: ‘utf8proc_property_t’ {aka ‘const struct utf8proc_property_struct’} has no member named ‘ambiguous_width’

After checking the source code of the utf8proc package, it was clear that the ambiguous_width property was only added on the version 2.10.0. At this point the rules are well know: create a new package definition, inheriting the existing one, with the proper version as a checkout tag. This time, the approach was not as straight forward as with tree-sitter, which generated a lot of new knowledge.

Building utf8proc@2.10.0

As expected, the initial approach to build a new version of this package was to just inherit the definition of the existing repository version (2.7.0 was the most recent available version on its own symbol):

(define-public utf8proc-2.10.0
               (package
                 (inherit utf8proc-2.7.0)
                 (name "utf8proc")
                 (version "2.10.0")
                 (source
                   (origin
                     (method git-fetch)
                     (uri (git-reference
                            (url "https://github.com/JuliaStrings/utf8proc")
                            (commit (string-append "v" version))))
                     (file-name (git-file-name name version))
                     (sha256
                       (base32 "1n1k67x39sk8xnza4w1xkbgbvgb1g7w2a7j2qrqzqaw1lyilqsy2"))))))

The first build produced a clear error: julia: command not found. Using the previous Neovim package definitions as example, this error should be fixed with adding the julia package to its native-inputs (differences between inputs and native-inputs are explained on the official guix documentation):

(native-inputs
  (modify-inputs
    (package-native-inputs utf8proc-2.7.0)
    (append julia)))

A new round of package building produced a new error:

line 0: failed !iscase(10fc) in data/Lowercase.txt

This one took some time to solve, as finding the reason behind it was rather quick, but proper fixing proved more difficult that it seemed. Investigation on both the package codebase and the guix definition of previous versions, raised a probable suspect:

The package build process fetched some required files whose URL was composed from a defined UNICODE_VERSION local variable. The inherited package version set it to version 14.0, while the Makefile of the new utf8proc version required the UNICODE_VERSION to be 16.0. The initial suspicion was then proved after a successfully build of the new package version within a guix shell with its dependencies.

The file that was causing the error was the DerivedCoreProperties.txt, so I applied the modify-inputs procedure, like before, to replace that dependency with the new definition using the correct UNICODE_VERSION updated. I won't present the code here, as that approach didn't stuck on me. During the investigation on the current guix package definition I noticed that, although the build was successful, the other required files were still being fetched from the original package definition, which used the unicode version 13.0. With that realization, I decided to declare a new list of native-inputs, for this new version, with all the required dependencies updated:

   (native-inputs
      (let ((UNICODE_VERSION "16.0.0"))
        `(("DerivedCoreProperties.txt"
           ,(origin
              (method url-fetch)
              (uri (string-append "https://www.unicode.org/Public/"
                                  UNICODE_VERSION "/ucd/DerivedCoreProperties.txt"))
              (sha256
                (base32 "1gfsq4vdmzi803i2s8ih7mm4fgs907kvkg88kvv9fi4my9hm3lrr"))))
          ("NormalizationTest.txt"
          ,(origin
             (method url-fetch)
             (uri (string-append "https://www.unicode.org/Public/"
                                 UNICODE_VERSION "/ucd/NormalizationTest.txt"))
             (sha256
              (base32 "1cffwlxgn6sawxb627xqaw3shnnfxq0v7cbgsld5w1z7aca9f4fq"))))
         ("GraphemeBreakTest.txt"
          ,(origin
             (method url-fetch)
             (uri (string-append "https://www.unicode.org/Public/"
                                 UNICODE_VERSION
                                 "/ucd/auxiliary/GraphemeBreakTest.txt"))
             (sha256
              (base32 "1d9w6vdfxakjpp38qjvhgvbl2qx0zv5655ph54dhdb3hs9a96azf"))))
          ;; For tests
          ("perl" ,perl)
          ("ruby" ,ruby)
          ("julia" ,julia))))

With this final utf8proc package definition, neovim@0.11.0 finally built successfully and became ready to be used.

Although it seems like it was actually not that much difficult to built this package definition, aside all the code reading and researching to reach the final result, there was a big obstacle that also brought some knowledge with it:

To move faster, when defining the native-inputs of this package, I was just building it with the updated git tag, hoping that it failed to verify the provided hash. guix gives the expected hash when this type of verification fails, so I can update it on the package definition and move on.

sha256 hash mismatch for /gnu/store/q9822pzv9s7rb8acgfm1b7vhp2nppfc5-DerivedCoreProperties.txt:

expected hash: 0gfsq4vdmzi803i2s8ih7mm4fgs907kvkg88kvv9fi4my9hm3lrr                                                                 

actual hash:   1gfsq4vdmzi803i2s8ih7mm4fgs907kvkg88kvv9fi4my9hm3lrr

However I didn't noticed that, as the naming of the files were the same for all versions, guix saved them in its store with the provided hash as part off the generated store name. With my laziness of reusing the hash of the previous versions, the build process just kept using the file it already fetched for the original package, and the error kept happening. Only after a long debug and bug searching process I reached this conclusion, changed a random character on the declared hash, and the verification failed with my long awaited expected value to use.

Moving to a personal channel

After all this work and learning, and having a fully functional build for neovim@0.11.0, I decided that it deserved to be on a personal guix channel, which could then be used by other people. This would also enable future me to use the same approach on other applications and quickly reuse the work on any machine by only issuing guix pull (provided that the channel is configured).

Creating a channel was extremely easy by following the official documentation. All the package definitions are guile modules with the lp packages namespace. The channel content is located under the .guix/modules sub-directory so it's possible to use the channel repository for other functions in the future.

The end result can be found on codeberg and the channel can be added to any channel list.

Conclusions

This was, without a doubt, a great adventure. It enabled me to learned more about the guix build process, its package definitions and how to extend an existing definition to build different versions. It also taught me how to interpret guix errors, which I'm sure will become handy really soon, and iterate over them.

Creating valid guix package definitions proved that the knowledge I've been gathering on the guile scheme language is also very useful to dynamically customize my systems.

The work done here was just to upgrade existing packages, but it made me more confident to eventually make the same for new ones, where the build process would need to be defined from scratch.

I also have my own channel now, which will be very useful for testing builds on my own systems and even make my own software available through it. And I believe it will help a lot when I decide to try to deploy my own substitute channel.

But the work done here shouldn't be only available for me of for whoever uses my channel, so the obvious next step is to understand how to contribute to guix and try to make this available to everyone on the main channel.

https://luispereira.dev/blog/building-neovim-on-guix.html
Joining the Craftering
Show full content

It's official 🎉, this blog is now part of the SystemCrafters community webring, which is a mark of great importance to me. Not only because it makes it more real, adding the responsibility on myself of keeping it alive from now on, but also because it was added to the ring on its first anniversary, as the twentieth member. This event also deserved a better reading experience, so I'm deploying my own carefully crafted theme.

But what is a webring?

In simple terms, a webring is a group of sites that are linked together in a circular navigation approach. Each site have links to two other sites, the previous and the next on the circular ring (in this blog the links are located at the footer of every page). Assuming that all sites in the webring are somehow related in content, this enables visitors to quickly discover other sites that could be of their interest.

But, to be honest, there is no best way to learn more about this than reading the article from shom, the creator of the craftering.

Why did I join?

I came across the SystemCrafters community some years ago, on my journey to learn a little more about emacs and LISP based languages, and found a very dynamic and helpful community. David Wilson's streams were (and still are to this day) very real and funny (special when debugging issues), which very quickly became a real pleasure to watch and take part on the community discussions going on the IRC channel. SystemCrafters also arrived at a time I was really needing a shift on my professional career to find pleasure and fun on what I was doing on my day to day, which all of them, unknowingly, made a real difference.

Ironically, that career shift really happened and, due to the new learnings and personal improvements it required, pushed me to become a distant follower of the community for sometime.

But this transition settled and my interest in the LISP universe found its way back on my head, proving that the time to get close to the community has arrived. I'm now becoming more active, very happy for the way it has evolved and the new people I met, hoping to eventually contribute for what they are doing. I'm learning my way through guile, guix and even common lisp, and have some ongoing projects on this direction and decided to start my contributions with joining the craftering promising to document my journey and learnings, not only for any visitor who find them useful but also for me to know the path that I personally have traveled.

Improving the blog UI

After the initial release of this blog, I promised an UI improvement for the reader to enjoy my content. Joining the craftering made me want to speed this implementation as more people would be able to reach the site and I wanted to give a good reading experience and keep the new visitors interested in my next writings.

So diving again in the haunt, and using glenneth's and dthompson's own sites as examples, I refactored my haunt configuration to adapt my previous site layout.

The first step was to create my haunt theme, which basically consists in creating the sxml structure that will produce the html that represents each of the static pages generated. This is accomplished by setting three procedure keys:

  • #:layout, which holds the template definition of the whole HTML page, and where the body of the page is supplied on the body argument;
  • #:post-template, that holds the template that builds the HTML of the content of a blog post. For example, using commonmark as the source of content, this template defines how that parsed content is rendered on the page;
  • #:collection-template, which configures, for example, how a list of existing posts is rendered on the page.

I used these procedures to organize the page content as I wanted, introducing some CSS classes when needed. The result was actually very clean and simple, which really surprised me, but also remembered me that we can go pretty far and really quick if the focus is on what it really matters. This site is basically built using the following configuration:

  (theme #:name "easilok-main"
         #:layout
         (lambda (site title body)
           `((doctype "html")
             (head
              (meta (@ (charset "utf-8")))
              (title ,(if title
                          (string-append title " :: easilok.dev")
                          (site-title site)))
              ;; css
              (link (@ (rel "stylesheet")
                       (href "/assets/css/main.css")))
             (body
               ,easilok-header
               (hr (@ (class "separator")))
              (div (@ (class "main-content"))
                   ,body)))))
         #:post-template
         (lambda (post)
           `((article
               (h1 (@ (class "post__title"))
                   ,(post-ref post 'title))
               (div (@ (class "post__date"))
                    ,(date->string (post-date post)
                                   "~d ~b, ~Y"))
               (div (@ (class "post__tags"))
                    ,@(build-tags-list post))
               (div (@ (class "post__content"))
                    ,(post-sxml post)))))
         #:collection-template
         (lambda (site title posts prefix)
           (define (post-uri post)
             (string-append prefix "/" (site-post-slug site post) ".html"))
           `((h1 ,title)
             ,(map (lambda (post)
                     (let ((uri (post-uri post)))
                       `(article (@ (class "feed__post"))
                                 (h2 (a (@ (href ,uri))
                                        ,(post-ref post 'title)))
                                 (div (@ (class "feed__post-date"))
                                      ,(date->string (post-date post)
                                                     "~d ~b, ~Y"))
                                 (div (@ (class "feed__post-tags"))
                                      ,@(build-tags-list post))
                                 (div (@ (class "feed__post-summary"))
                                      ,(first-paragraph post))
                                 (a (@ (href ,uri)) "read post →"))))
                   posts)))))

                   

Naturally there are CSS definitions behind (and some other additions for the craftering to work), but those are not that much complicated and can be better viewed at the code.

Notes:

  • As I saw different behavior of the UI on different browsers, I decided to use normalize.css to improve standardization.
  • Although most of the layout was migrated from my previous site, I tried to experiment with a new color set which I took inspiration from the paperheartdesign.
Conclusions

First and most important, happy birthday to the craftering and thanks to the SystemCrafters community for making me want to write about what I'm doing and learning.

I'm really enjoying my path and learnings, and I'll soon release some articles of what I was capable of doing with guile and in guix. Making this site with haunt has been a breeze and I'm already planning the next additions like a proper frontpage with introduction for my self and post filtering by tag.

See you soon crafters.

https://luispereira.dev/blog/joining-the-craftering.html
Haunting this Blog
Show full content

Aligning with the previous posts, what better way is there to build this blog that using guile scheme? Well, maybe a lot, but we're here for the fun in learning new approaches, so I'm committed to build this blog using haunt, an "hackable static site generator written in Guile Scheme".

My main goal on this journey is to start small, right from the beginning. This means that, at the time of this post, the blog is using a very simple haunt configuration, without any theme customization, making it really raw.

Whenever new customizations are added to the blog (and deployed), I'll ensure that a new post is created explaining what I've done. So in practice, the reader will be walking this path with me, and hopefully learning something along the way.

At any checkpoint, including this one, I'll leave a couple of images showing any relevant layout that was available at the moment, providing visual guidelines of the impact of the changes I made.

My initial haunt configuration is very trivial and only defines:

So let's walk this learning path together and sorry for any bad UI parts at the moment. I will definitely improve it 😉.

Next step: probably adapt my old personal website theme for this blog, which eventually will replace the whole site.

Theme screenshots
https://luispereira.dev/blog/haunting-this-blog.html
Trying Guix Package Manager
Show full content

From debian, it is possible to install the guix package manager from apt. This will install whatever version is present on the repositories. But after installation, a simple guix pull will update it to the latest version (although it can take some time to finish depending on the difference to the newest version)

After that it's pretty much just follow the guix startup documentation, starting with the application setup.

Locales

Starting with the locales installation, I opted to reduce the number of locales installed for my usage, which resulted in this my-locales.scm file:

(use-modules (gnu packages base))

(make-glibc-utf8-locales
  glibc
  #:locales (list "en_US" "pt_PT")
  #:name "glibc-my-utf8-locales")

And then run the guix package --install-from-file=./my-locales.scm will install this trimmed version of the used locales. The environment variable GUIX_LOCPATH referred on the installation guide was already set by the debian installation by the /etc/profile.d/guix.sh.

SSL certs

Applications like git uses SSL encrypted connections, which requires the presence of the Certificate Authorities in the system. On Guix this is accomplished with the nss-certs and it's required or the following error will appear when using git:

server certificate verification failed. cafile: none crlfile: none guix

After that, each application uses a different environment variable to set where to look for those certificates. For git and curl the following was set (along with a general used one):

export SSL_CERT_DIR="$HOME/.guix-profile/etc/ssl/certs"
export SSL_CERT_FILE="$HOME/.guix-profile/etc/ssl/certs/ca-certificates.crt"
export GIT_SSL_CAINFO="$SSL_CERT_FILE"
export CURL_CA_BUNDLE="$SSL_CERT_FILE"

These environment variables need to be always available on the shell used by GUIX, so they are normally set on the .bashrc, .zshrc or, eventually, on your .profile if the file is loaded by your shell.

I prefer a more organized way to handle those, which I'll present later on this post.

Installing applications

Following the getting started, applications can be installed on the system with guix install <package>.

However, in order to follow the advantage of the GUIX declarative setup features, as well as it's reproducibility, I setup custom profiles and use a manifest file to install the applications that I required.

For this custom profile approach to work, there is 3 steps that I setup:

1. Custom profile load

This basically follows an official guide for profiles, where it loads every profile folder located in a specific location.

In this example, the location $HOME/.guix-extra-profiles will contain folders corresponding a different profiles. GUIX populates a /etc/profile file, inside each profile folder, that can be sourced to update all shell environment variables to include what it provides.

GUIX_EXTRA_PROFILES=$HOME/.guix-extra-profiles
for i in $GUIX_EXTRA_PROFILES/*; do
  profile=$i/$(basename "$i")
  if [ -f "$profile"/etc/profile ]; then
    GUIX_PROFILE="$profile"
    . "$GUIX_PROFILE"/etc/profile
  fi
  unset profile
done
2. Create manifests files

Each custom profile is then ruled by a manifest file which contains the packages it should contain.

The following example can be a development.scm manifest file:

;; -*- mode: guix-scheme-*-

(specifications->manifest
  '(
    "neovim"
    "zsh"
    "zsh-autosuggestions"
    "zsh-syntax-highlighting"
    "zsh-completions"
    "ripgrep"
    "git"
  ))
3. Activate and update custom profile

The presented GUIX guide also instructs how to create a profile based on a manifest file. Taking the development example previous presented (the inner folder with the same name is required):

mkdir -p $HOME/.guix-extra-profiles/development
guix package --manifest=development.scm --profile=$HOME/.guix-extra-profiles/development/development 

As any other workflow, this could be automatized, so I created (or practically steal it from benoitj from System Crafters) a script that:

  • Called without any arguments will search for all manifest files in $HOME/.config/.guix/manifests and activate a profile based on the file name, installing or updating all specified dependencies.
  • Called with a any number of arguments, will try to find a manifest file, for each argument, at the same folder and activate the profile with the same name.
  • Activating a profile means to create the folder, if it does not exist, and install ou update all specified applications to the latest known version to the system.

The script has also some colorful outputs of what it is doing.

#!/usr/bin/env bash

GREEN='\033[1;32m'
RED='\033[1;30m'
NC='\033[0m'

profiles=$*
if [[ $# -eq 0 ]]; then
    manifests="$HOME/.config/guix/manifests/*.scm";
    newProfiles=()
    for manifest in $manifests; do
        filename=$(basename $manifest)
        filename="${filename%.*}"
        newProfiles+=($filename)
    done
    profiles=${newProfiles[*]}
fi

echo
echo -e "${GREEN}Using profiles: "$profiles" ${NC}"
echo

for profile in $profiles; do
  profileName=$(basename $profile)
  profilePath="$GUIX_EXTRA_PROFILES/$profileName"
  manifestPath=$HOME/.config/guix/manifests/$profile.scm

  if [ -f $manifestPath ]; then
    echo
    echo -e "${GREEN}Activating profile:" $manifestPath "${NC}"
    echo

    mkdir -p $profilePath
    guix package --manifest=$manifestPath --profile="$profilePath/$profileName"

    # Source the new profile
    GUIX_PROFILE="$profilePath/$profileName"
    if [ -f $GUIX_PROFILE/etc/profile ]; then
        . "$GUIX_PROFILE"/etc/profile
    else
        echo -e "${RED}Couldn't find profile:" $GUIX_PROFILE/etc/profile "${NC}"
    fi
  else
    echo "No profile found at path" $profilePath
  fi
done
Conclusions

With this approach I now have a functional guix setup, at least as package manager on any Linux distro, where I can group packages in manifest files and reuse them as needed in different computers, and started using it already on my work computer and remote development server.

Next step will be to include home configuration in my setup and migrate at least some of my personal configurations to it.

https://luispereira.dev/blog/trying-guix-package-manager.html
Blog.Init()
Show full content

So, it's finally happening. For quite some time now, I'm saying I should start a blog. The reason behind it is mainly to put out to the world the knowledge I'm gathering on my day to day projects. This will not only pass my learnings to anyone who finds this blog, but will also serve as a small knowledge base that I can get back anytime in the future to remember some topic.

I'm a very curious person and I'm always learning new concepts, trying new technologies and tools and challenging myself on uncomfortable grounds. This way of life brings me new knowledge frequently and I make a lot of small and useless things that I don't give any credit because they are just experiences I have to reach a goal. But I would like that to change.

Every obstacle I reach, every research I make in order to find a way to solve my issues, it's a path and it can be helpful for anyone. We live in a world where most people show only the good results, the end goal, hiding away the troubles and obstacles that they had to overcome. And I personally think that this not only hides part of the learning but it also increases the feeling of self doubt and that one cannot reach the same place in life that others.

So I'll try to document every mistake, every blockage, and what was the process taken to fix or surpass it. I hope it helps someone, someday, somehow.

The blog itself will be a learning process for me. Currently I'm invested in learning guile scheme, with a personal project that will make some articles in the first times, and common lisp, by reading the Learn Lisp The Hard Way by "the Phoeron" Colin J.E. Lupton, an online book that was shared on System Crafters community.

I found the System Crafters community some years ago, while trying to understand Emacs (I'm a long time VIM/Neovim user), and created a special connection with the family of lisp languages. As I was invested in professional growth, I never deepened my knowledge on it. But I feel that now was the time and returned to the community with a great coincidence of David Wilson also focused on a guile scheme project, which I'm proudly recommend for anyone who wants to view a real project using it.

So, without further delay, welcome and let's have an awesome ride on this journey and sorry for all mistakes there I'll be doing in the future.

https://luispereira.dev/blog/bloginit.html