[HN Gopher] Fennel libraries as single files (2023)
       ___________________________________________________________________
        
       Fennel libraries as single files (2023)
        
       Author : todsacerdoti
       Score  : 59 points
       Date   : 2025-08-13 04:03 UTC (18 hours ago)
        
 (HTM) web link (andreyor.st)
 (TXT) w3m dump (andreyor.st)
        
       | fredrikholm wrote:
       | Single file dependencies are amazing. I've never understood why
       | it's so unpopular as a distribution model.
       | 
       | They can be a bit clunky in some languages (eg. C), but even then
       | it's nothing compared to the dystopian level of nightmare fuel
       | that is a lot of dependency systems (eg. Maven, Gradle, PIP).
       | Free vendoring is a nice plus as well.
       | 
       | Lua does it right; happy to see some Fennel follow in that
       | tradition.
        
         | Bratmon wrote:
         | The main reason you don't see it that often is because of the
         | "what if some extremely common library that we depend on
         | indirectly 63 times at a total of 11 different versions
         | discovers that four of those versions have a major security
         | vulnerability" problem.
         | 
         | For hobby projects, vulnerable dependencies are usually a minor
         | annoyance that's often less annoying than dealing with more
         | elaborate dependency systems.
         | 
         | For big professional projects, not being able to easily answer
         | "are we currently running any libraries with major known
         | vulnerabilities" makes this approach a non-starter.
        
           | fredrikholm wrote:
           | Cheers!
           | 
           | You mean in terms of having one centralized source of truth?
           | I find this exact same problem with dependency systems as
           | well; every project has their own dependency tracking file,
           | and unless there is a deliberate attempt at uniting these
           | into one trackable unit, you get this exact mess either way.
           | 
           | If the problem is automation (limiting human factors), then
           | I'd say that whatever issue exists with single file
           | dependencies is a tooling issue more than anything else.
           | There's nothing about having one file that makes this any
           | harder. In fact I'd say the opposite is true (eg. running
           | checksums).
           | 
           | The one thing that dependency systems have going for them, is
           | homogenized standardization. I'd rather _go install x_ than
           | whatever Ninja-generating-CMake-files-generating-Makefiles-
           | that-generates-files-that-produce-a-final-file carnival rides
           | that linger from the past. Perhaps its because of those scars
           | that I like single dependency files even in  / especially in
           | larger projects.
        
             | Bratmon wrote:
             | Clarification question: When you say "dependency systems,"
             | are you referring to PIP/NPM/go get/Cargo or just the C/C++
             | clusterfuck?
             | 
             | Because under the hood "go install" (and NPM/PIP/Cargo)
             | makes sure that if you indirectly depend on a library, you
             | only have one version* of that library installed, and it's
             | pretty easy to track which version that is.
             | 
             | That's the key difference: With "go install" you only have
             | one version of your indirect dependencies, with single-file
             | libraries, you have one version for each thing that needs
             | them and have no real way of tracking indirect
             | dependencies.
             | 
             | I'm not going to try to defend the C/C++ clusterfuck.
             | Switching to single-file libraries would improve that
             | because any change would improve that.
             | 
             | * Sometimes one version of each major release
        
           | whstl wrote:
           | I think this comment shows the difference between web
           | development and other industries.
           | 
           | This problem is not existent in languages that traditionally
           | use single-file libraries because projects rarely get to the
           | point where they use 63 libraries, let alone have 63 indirect
           | dependencies to a library.
           | 
           | Also: popular single-file libraries in the tradition of stb
           | won't even have dependencies to other libraries of the sort,
           | only to the standard library.
        
             | Bratmon wrote:
             | If your dependency graph is simple enough that you don't
             | have any indirect dependencies, then it really doesn't
             | matter what you use for dependency management.
             | 
             | Modern dependency management tools are designed for more
             | complicated cases.
        
               | whstl wrote:
               | I would argue that it's the opposite. It's "more
               | complicated cases" that are designed to fit modern
               | dependency management.
        
               | Bratmon wrote:
               | If the goal of your dependency system is to reduce the
               | number of dependencies that library authors include,
               | isn't encouraging people to make single-file libraries
               | counterproductive?
        
           | CyberDildonics wrote:
           | How would that be different if you have a source file split
           | up into multiple files?
           | 
           | Having a list of what version you're using of a single file
           | library seems like an easy problem to solve. If nothing else
           | you could put the version number in the file name and in a
           | debug build print off the file name.
        
             | Bratmon wrote:
             | I'm not comparing single-file vs multiple files, I'm
             | comparing single-file vs NPM/PIP/go get/Cargo
             | 
             | Let's say you depend on foo, which depends on 10 other
             | libraries including bar, all of which depend on a library
             | called baz. Then one day someone discovers an exploit for
             | baz.
             | 
             | With npm, you only have one version of baz installed, and
             | can easily check if it's a vulnerable version.
             | 
             | With single-file libraries, baz was built into a single
             | file. Then bar was built into a single file containing baz.
             | Then foo was built into a single file containing bar and
             | other libraries, all which included baz.
             | 
             | Now your library contains baz 10 times, all of which might
             | be at different versions, and none of which you can easily
             | check. (You can check your version of foo easily enough,
             | but baz is the library with the exploit)
        
               | sitkack wrote:
               | > I'm not comparing single-file vs multiple files, I'm
               | comparing single-file vs NPM/PIP/go get/Cargo
               | 
               | You are talking about your own thing. Everyone else is
               | talking about the number of files, not the distribution
               | and update mechanism.
               | 
               | The packaging system could support single file and still
               | be able to track versions and upgrades. The JVM ecosystem
               | is effectively single file for many deps esp now that
               | jars can contain jars.
        
               | Bratmon wrote:
               | The comment I replied to was "They can be a bit clunky in
               | some languages (eg. C), but even then it's nothing
               | compared to the dystopian level of nightmare fuel that is
               | a lot of dependency systems (eg. Maven, Gradle, PIP).
               | Free vendoring is a nice plus as well."
               | 
               | How is that not talking about PIP?
        
               | CyberDildonics wrote:
               | Like the other person said, you're mixing up single file
               | libraries with having no package manager or dependency
               | management.
               | 
               | That being said in C and C++ the single file libraries
               | typically have no dependencies, which is one of the major
               | benefits.
               | 
               | Dependencies are always something that a programmer
               | should be completely clear about, introducing even one
               | new dependency matters and needs to be thought through.
               | 
               | If someone is blasting in new dependencies that
               | themselves have new dependencies and some of these
               | overlap or are circular, and nothing is being baked down
               | to standing alone it's going to end in heart break and
               | disaster no matter what. That basically the opposite of
               | modularity, a web of dependencies that can't be
               | untangled. This applies to libraries and it applies on a
               | smaller scale to things like classes.
        
               | Bratmon wrote:
               | If the goal of your dependency system is to discourage
               | people from adding dependencies, then isn't supporting
               | single-file libraries counterproductive because it makes
               | it easier to add new dependencies?
        
         | whstl wrote:
         | I actually find them to be extremely popular in C and C++, and
         | also in some Lua communities.
         | 
         | They just have an extremely vocal opposition.
         | 
         | This is not too dissimilar to static builds and unity builds,
         | which also "make your life easier" but people will write
         | tirades about how dangerous they are.
         | 
         | I wonder if C++ modules (which I'm loving) will also get the
         | same treatment, since they make a lot of clunky tooling
         | obsolete.
        
           | CyberDildonics wrote:
           | I like to combine the two and put a lot of single file
           | libraries into one compilation unit. It compiles fast and
           | puts lots of dependencies in one place that doesn't need to
           | be recompiled often.
        
           | silvestrov wrote:
           | I think SQLite's amalgamation is one of the reasons SQLite is
           | so popular for embedding.
        
         | norir wrote:
         | I suspect much of this is a historical result of c's
         | compilation model. Since c compilers define a compilation unit
         | as a file, there is no good way to do incremental compilation
         | in c without splitting the project into multiple files. In an
         | era of much slower computers, the incremental compilation was
         | necessary so this was an understandable choice.
         | 
         | For me, today this split is almost always a mistake. Having
         | everything in one file is superior the vast majority of the
         | time. Search is easy and it is completely clear where
         | everything is in the project. Most projects written by a single
         | individual will be fewer than 10K lines which many compilers
         | can clean compile in less than one second. And I have reached
         | the stage of my programming journey where I would rather not
         | ever work on sprawling hundred K + line projects written by
         | dozens or hundreds of authors.
         | 
         | If the single file gets too unwieldy, splitting it in my
         | opinion usually makes the problem worse. It is like cleaning up
         | by sweeping everything under the rug. The fact that the file
         | got so unwieldy was a symptom that the design got confused and
         | the single file no longer is coherent. Splitting it rarely
         | makes it more coherent.
         | 
         | To make things more concrete and simple. For me the following
         | structure is strictly better (in lua like the op)
         | foo.lua:            local bar = function() print "bar" end
         | return function()              print "foo"              bar()
         | end
         | 
         | compared to                   bar.lua:           return
         | function() print "bar" end         foo.lua:           local bar
         | = require "bar"           return function()             print
         | "foo"             bar()           end
         | 
         | In the latter, I now both have to keep track of what and where
         | bar is and switch files to see its contents or rely on fancy
         | editors tricks. With the former, I can use vim and if I want to
         | remind myself of the definition bar, it is as easy as `?bar =`.
         | I end up with the same code either way, but it is much easier
         | to view in a single file and I can take advantage of lua's
         | scoping rules to keep module details local to the module even
         | from other modules defined in the same file.
         | 
         | For me, this makes it much easier to focus and I am liberated
         | from the problem of where submodules are located in the
         | project. I can also recursively keep subdividing the problem
         | into smaller and smaller subproblems that do not escape their
         | context so that even though the file might grow large, the
         | actual functions tend to be reasonably small.
         | 
         | That this is also the easiest way to distribute code is a nice
         | bonus.
        
         | JonChesterfield wrote:
         | It takes a tiny bit of lateral thinking in C.
         | 
         | What if "build" hacked all the source into a single text file,
         | instead of hacking it all into a single archive of object
         | files?
         | 
         | Roughly, write static in front of most functions, don't reuse
         | names between source files, cat them together in some viable
         | order.
         | 
         | Now you can do whatever crazy codegen nonsense you want in the
         | build and the users won't have to deal with it. The sqlite
         | amalgamation build is surprising, using the result is trivial.
        
       | eXpl0it3r wrote:
       | Yet another programming language (I haven't heard of):
       | https://fennel-lang.org/
        
         | jamesblonde wrote:
         | Thanks.
         | 
         | I thought it was the feature engineering company, Fennel, that
         | was acquired by Databricks this year.
        
       ___________________________________________________________________
       (page generated 2025-08-13 23:02 UTC)