[Haskell-cafe] Imports as first class functions

Evan Laforge qdunkan at gmail.com
Sun Dec 8 02:51:39 UTC 2024


Nix is an example of a language with first class imports, `import` is
a plain function the returns the "module" which is just a record like
any other.

Based on my experience, it's not all that great.  Of course it could
be because the nix language itself is weak, with dynamic typing and no
user defined data types.  But the main effect is that it's hard to
tell who imports what or where some value comes from, because import
may syntactically occur in any expression, and then the module may be
passed around arbitrarily far before being used.  As opposed to
haskell where you just look at the top of the file and a single step
takes you to the definition.  An advantage is that of course a
"module" can just be a function which returns a module, which is
similar to how I imagine ML functors being, except all in the value
language.  It's used to conveniently inject a bunch of variables into
scope.  Since there are no user-defined data types though, none of the
traditional "define a hash map with built-in hash function" type
stuff.  I've (very) occasionally wanted a parameterized module in
haskell, the closest analog is just a normal record, and you can then
use RecordWildCards like a local import.  It would be nice for DSLs if
records and modules could be unified that way, so you could have both
local imports and parameterized modules.  I haven't really come up
with a use for it outside of one particular DSL though.  I gather
there's a deep history for this in ML though, and I recall some
dependently typed languages like Agda or Idris had unified modules and
records to a greater degree than haskell.

For GHC API, I have a program that uses it to implement a REPL which
is then the main text-oriented UI.  I originally wanted to use dynamic
loading to be able to write and insert code at runtime, but never got
that going.  Instead it's reasonably fast to rebuild and relink and
reload the program, which seems like a less finicky way to go about
it, and I settle for the REPL which compiles fragments to modify the
state, but are not themselves persistent unless I paste them into a
module somewhere.


More information about the Haskell-Cafe mailing list