UP | HOME

Configuring Neovim with Lua

Before we begin, it is crucial to realize and understand all the fundamental problems of amaterur open-source projects.

The fundamental problems are:

This incomplete list, however, can be reduced to just one generalized observation - no one takes it seriously, and any serious effort takes time.

There is also lack of collaboration among developers. Common APIs has to be designed together with the potential users and evolve quickly based on a feedback.

The classic examples of good design are the standard libraries of Ocaml, Haskell, and Go. But these are slowly (20 years) evolve within competent communities.

The ancient maxims of programming

One of the most important principle is that one does not have to read the code (representation and implementation) to understand how it works.

This is the principle. From it literally everything else (information hiding, encapsulation, abstract interfaces, and modularity in general) follows.

From this principle the whole proper methodology has been developed, which states that partitioning – abstract interfaces of data-types – has to be done first, postponing and hidding the representation and implementation details.\

This methodology goes back to Dijkstra, (the worst CS witter ever) and has systematized and popularizes by B. Liskov and later by Gregor Kiczales.

Why have we talk about the classics? Because, it seems, that we have the “garbage in – garbage out” (junkfood) problem otherwise, and there is no solution, except making the right effort.

The ancient maxims says:

  • Data dominates (the algorithms follow from the recursive structure of the data).
  • use “data abstraction” – Abstract Data Types
  • use “procedural abstraction” – Parameterize everything (including the types).
  • write formal specifications” – so we do not have to read the code
  • use all the tools available, especially static analyzers and model-checkers (TLA+)

Sadly, most of individual devs on github never heard of these, so the results are abismal.

Coping with the mess

There are a few important things to realize.

Dependencies

All the dependencies of a module has to be installed before the module can be loaded and configured.

The installing aspect refers to actually have a copy of the code on a disk, so it can be loaded into memory.

Loading

The module has to be loaded after all its dependencies were loaded and properly configured, otherwise the results are random.

There are at least two types of configuration - before the actual loading and after the module has been loaded.

Calling

To call a misconfigured module will return an arbitrary or even random results.

This bring us to the notion of an implicit ordering in your configuration files.

Ordering

There are at least two implicit dependency graphs - what calls what (dependencies) and what loads prior to what.

The problem with so-called lazy (on-demand) loading is that a module can be loaded either unconfigured or misconfigured.

Every time it can be a different subtle problem.

“Declarative” configuration

There is a fundamental problem with “declarative” - the notion of an actual ordering of events has been lost.

One either uses nesting of expressions to establish an explicit order, or sticks to imperative configuration, which is the same as an imperative program (with all the implied problems).

An unconfigured module is a just the same problem as a declared but uninitialized (yet) variable (some memory location) in an imperative language, and one cannot tell is a module has been properly configured (initialized) or not.

To make everything almost “intractable”, modules can be loaded after “configuration” is supposedly done – on (triggered by) particular events.

In fact the individual configuration “function” is not called until the module is actually being loaded, so other modules which assume that their dependencies are already loaded and configured could be screwed.

In short, declarative and imperative cannot be mixed, since the notion of mutations in time is definitive for an imperative code, and does not exist, even as a concept, in a purely declarative code. Seems like very few people understand this.

So, do not assume any particular ordering and just use configs as part of an imaginary “conceptual closure” - the whole chain (a graph) will be “triggered” eventually. Yes, pure-functional thinking for actual imperative, mutable crap.

And, yes, the order in which individual modules appear in a Lua-based config file matters, even if it looks like declarative, because it will be interpreted in a linear order and mutations will occur in that order.

In theory the config, written in a declarative DSL should be interpreted by its own interpreter into an immutable graph of “expressions”, and then this graph shall be reduced by a “configuration runtime”, using Lua runtime. Yes, that would be a pure-functional DSL with an interpreter and a corresondig graph-reduction “runtime”.

Delegating

Yes, delegation to some well-maintained package or a set of libraries is how, at least in the theory/ we should try to manage complexity and all the irrelevant details.

This is just like we “delegate” cooking of our food to restaurants and takeaways, having some vague understanding of how every dish is cooked and from which ingredients.

It is so good to have an off-self medicines and electronic devices, lets say, without necessity to understand of how exactly it has been made and what is inside.

In reality, however, what we get is usually an low-effort amateur spaghetti crap, put together in a hurry, without much understanding. So we get a lowest quality junkfood.

Almost every individual project we see on Github is such an amateur crap, which is ok, given that people do it some times for fun and mostly to show off and to inflate their self-esteem.

So we have to watch out.

Google

It is well-known that Google is, arguably, is the most advanced and sophisticated programming shop in the whole world.

The Chrome browser, for example, has more details and more of “layered structure” that a Boeing 747. This is not an exaggeration – modern web browsers are incredibly complex artifacts.

So how Google accomplishes this? Well, it pays attention to every detail, carefully select and then maintain all their open-source dependencies, including compilers and even the C++ standard library.

This, of course, is the universal approach. If you want to run a Michlen restaurant or a top tier Sushi shop, you have to do the same – carefully select the ingredients and pay attention to details.

You can be absolutely sure, that Boeing or NASA have the same fundamental principles applied and designed their internal “processes” based on them.

Select carefully

This is what I am trying to talk about. This guy has his Neovim configuration done from the ground up, without copy-pasting from others (while almost every config on github is just copy-pasting).

https://github.com/VonHeikemen/dotfiles

As a result, suddenly, it is small, well structured, with carefully selected names and is easy to understand. This is what one have to select as an ingredient.

The problem is that one usually have no idea that something can be done from scratch in a principle-guided manner with careful attention to details and emphasis on doing the right thing (from a mathematical CS-theoretical standpoint).

The pioneers of our craft did everything this very way, and you can tell! (Lotus 123 for DOS, UNIX vi, GNU Emacs, LaTex, X11R6, MIT Scheme, etc, etc).

Author: <schiptsov@gmail.com>

Email: lngnmn2@yahoo.com

Created: 2023-08-08 Tue 18:38

Emacs 29.1.50 (Org mode 9.7-pre)