Trying Fennel

Published Sun Jan 21 2024

Fennel is a small lisp-like language that compiles to Lua. I have become very interested in lisp and lisp-like languages, the syntax is simple and has great potential for elegance. Since Neovim is configured with Lua, we can use Fennel to write Neovim config with a lisp syntax!

Getting Started

Since Fennel is not officially supported, I am using nfnl to seamlessly integrate fennel with my config files. The setup here is very quick and easy. Just create a new file named .nfnl.fnl in your configuration directory and install "Olical/nfnl" using your plugin manager. From here any Fennel file you create under your configuration directory will be compiled to Lua for use by Neovim.

Translating a Lazy Plugin

Since I am using Lazy I have my plugins separated into modules under /lua/plugins/*.lua. I’ll use one of these as an example for Fennel’s syntax.

return {
    'rose-pine/neovim',
    name = 'rose-pine',
    lazy = false,
    priority = 1000,
    dependencies = {
        'f-person/auto-dark-mode.nvim',
    },
    config = function()
        require("rose-pine").setup({
            dark_variant = "moon"
        })
        local auto_dark_mode = require('auto-dark-mode')
            auto_dark_mode.setup({
                update_interval = 1000,
                set_dark_mode = function()
                    vim.api.nvim_set_option('background', 'dark')
                    vim.cmd('colorscheme rose-pine')
                end,
                set_light_mode = function()
                    vim.api.nvim_set_option('background', 'light')
                    vim.cmd('colorscheme rose-pine')
                end,
            })
        auto_dark_mode.init()
    end
}

This is my module for theming, it includes a small configuration function for automatically switching themes when my system preference changes. With some quick refactoring the Fennel equivalent looks like this:

{
    1 :rose-pine/neovim
    :name "rose-pine"
    :lazy false
    :priority 1000
    :dependencies [:f-person/auto-dark-mode.nvim]
    :config (fn []
        (let [rose-pine (require :rose-pine)]
            (rose-pine.setup { :dark_variant "moon" }))
        (let [auto-dark-mode (require :auto-dark-mode)]
            (auto-dark-mode.setup {
              :update_interval 1000
              :set_dark_mode (fn []
                  (vim.api.nvim_set_option "background" "dark")
                  (vim.cmd "colorscheme rose-pine")
              )
              :set_light_mode (fn []
                  (vim.api.nvim_set_option "background" "light")
                  (vim.cmd "colorscheme rose-pine")
              )
        })
        (auto-dark-mode.init)
      )
    )
}

With this example there are a few syntax differences that I enjoy.

First is Fennel’s key/value syntax for tables. You declare pairs with :key-name followed by a value. This works out great, except when you mix named and unnamed values. In the above you may wonder why 1 appears. This is because the first entry is indexed with no key name. If there is a way to mitigate these numbered keys I’d love to know.

Following with key/value pairs, Fennel has a separate table syntax for sequential tables: []. These are similar to arrays, in this example :dependencies has a sequential table for a value containing 1 string f-person/auto-dark-mode.nvim.

I am still early in my lisp and Fennel adventure, but so far it has been great! Once my configuration is fully Fenneled out I look forward to sitting with it and molding the code to fit Fennel’s style better.