~technomancy/fennel#61: 
fallback to function call when both macros and functions exist in single namespace and macro is not found

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?

#Example:

The library lib has init.fnl and macros.fnl providing both functions and macros, where init.fnl provides function foo, and 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 foo:

>> (lib.foo)
Compile error: Compile error in unknown:3
  macro not found in imported macro module

Looking at compile1 in compiler.fnl I see that it calls macroexpand* on 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.

Status
RESOLVED FIXED
Submitter
~andreyorst
Assigned to
No-one
Submitted
a month ago
Updated
a month ago
Labels
macros

~technomancy a month ago

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.

~technomancy a month ago

What do you think about this as a fix for the completion issue? https://p.hagelb.org/completion-shadow.patch.html

~andreyorst a month ago*

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.mapv is not listed in completion list but exists in the clj table 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 do works, 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-macros should be changed instead to check if symbol is visible.

~andreyorst a month ago

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 nil instead, because macro is not really there.

I'd imagine setting an additional __index metamethod 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.

~technomancy a month ago

Ah, I see what you mean! The problem is that the entire clj identifier 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.

~technomancy REPORTED FIXED a month ago

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.

Register here or Log in to comment, or comment via email.