I've been asked this question in our local community: why macros and functions can be placed into single namespace, but can't be accessed simultaniously?
macros.fnl providing both functions and macros, where
init.fnl provides function
macros.fnl provides a macro
bar. Using auto completion feature in the REPL user sees this:
Welcome to Fennel 0.9.3-dev on Lua 5.4! Use ,help to see available commands. >> (local lib (require :lib)) nil >> (import-macros lib :lib.macros) nil >> (lib. lib.bar lib.foo
Which seems to work. Then user tries to use
>> (lib.foo) Compile error: Compile error in unknown:3 macro not found in imported macro module
compiler.fnl I see that it calls
ast, which will fail if macro is not found. Maybe we could easy this check allowing it to fall back and compile it as a function call if macro is not found? This will allow both functions and macros to coexist in the same namespace, meaning that macros will still have higher priority, similarly to how it is done now.
This is quite useful in case of libraries which provide both functions and macros, in case user of the library doesn't want to import things without namespace prefix.
I think this is just a bug in completion logic. For clarity's sake we need scope resolution rules to be clear and unambiguous, so the macros should shadow the locals. Allowing it to fall thru makes the code less readable, because you can't tell just by looking at it whether the call resolves to a macro or a function. It also makes "jump to definition" more complicated.
What do you think about this as a fix for the completion issue? https://p.hagelb.org/completion-shadow.patch.html
I think it will introduce more questions than answers:
Welcome to Fennel 0.9.3-dev on Lua 5.4! Use ,help to see available commands. >> (local clj (require :cljlib)) nil >> (import-macros clj :cljlib.macros) nil >> clj. clj.def clj.defmulti clj.empty clj.if-let clj.into clj.try clj.when-meta clj.with-meta clj.defmethod clj.defonce clj.fn* clj.if-some clj.meta clj.when-let clj.when-some >> clj.mapv #<function: 0x56152a7cf490> >> (clj.mapv clj.inc [1 2 3]) Compile error: Compile error in unknown:4 macro not found in imported macro module >> ((do clj.mapv) clj.inc [1 2 3]) [2 3 4]
clj.mapvis not listed in completion list but exists in the
cljtable as a key, and can still be accessed without any issue. However when we try to use it in the call position, we get an error. But wrapping it in
doworks, because we can access it as a variable, only not in the call position. I can imagine that if someone stumbles upon this it will look like a mistake to them in the same way how original issue seems to be a bug.
IMO, if you want to prevent this from happening,
import-macrosshould be changed instead to check if symbol is visible.
However, I don't really see the major issue with having both functions and macros being accessible from the same namespace, apart from the fact that if you try to pass a macro as a variable it would silently pass
nilinstead, because macro is not really there.
I'd imagine setting an additional
__indexmetamethod that would throw error upon access to a macro value, would partially solve this, but it would be a hidden semantic which is not the best thing to have in a simple module system we praise.
Ah, I see what you mean! The problem is that the entire
cljidentifier is shadowed inconsistently. That is definitely a bug.
IMO, if you want to prevent this from happening, import-macros should be changed instead to check if symbol is visible.
I don't want to prevent shadowing altogether; I just want to make the shadowing work consistently to how it works for everything else.
I don't really see the major issue with having both functions and macros being accessible from the same namespace
In the end that would make it two separate namespaces, if the same name can resolve to two different things depending on the context. So because Fennel is a lisp-1 we need to make every identifier resolve to a single thing. I think the most recent commits on main take care of this.