bakpakin / Fennel

Lua Lisp Language
https://fennel-lang.org
MIT License
2.42k stars 124 forks source link

REPL doesn't store `gensym`'ed variables? #480

Closed KPCCoiL closed 4 months ago

KPCCoiL commented 4 months ago

Suppose I have foo.fnl:

(local foo (gensym))

(print foo)

(fn initialize []
  `(local ,foo 10))

(fn show []
  `(print ,foo))

{: initialize : show}

and bar.fnl:

(import-macros {: initialize : show} :foo)

(initialize)
(show)

fennel bar.fnl prints

_1_
10

as expected.

However, when I try it in the REPL, calling initialize sets _1_ to nil, not 10. Weirdly, if I initialize twice, it becomes 10.

Welcome to Fennel 1.4.2 on PUC Lua 5.4!
Use ,help to see available commands.
>> (import-macros {: initialize : show} :foo)
_1_
nil
>> (initialize)
nil
>> (show)
nil

>> _1_
nil
>> (initialize)
nil
>> (show)
10

>> _1_
10
>>

The issue does not arise if you replace (gensym) in foo.fnl by (sym :test) (hence the title).

technomancy commented 4 months ago

Yes, you can see here where the repl locals-saving code specifically skips anything which came from a gensym: https://git.sr.ht/~technomancy/fennel/tree/main/item/src/fennel/repl.fnl#L41

The vast majority of locals that come from gensym should not be saved by the repl. I agree it's annoying that this causes the problem you've described, but I don't know how to distinguish between gensyms like this vs the ones which are noise from macroexpansion, destructuring, etc. Turning on locals-saving for all gensyms is probably not the right solution.

My initial inclination is just to advise against using gensyms in a case where you want this, but I don't have enough context to know the whole story.

KPCCoiL commented 4 months ago

Thanks for your response. It's okay if it is intentional, and I'm closing this.

I was just playing around with fennel macros and trying to make something like

(module) ; => (local _1_ {})

(local foo 10)
(export foo) ; => (tset _1_ "foo" foo)

(exports) ; => _1_

Actually, I realized similar functionality can be achieved without this sort of gensyms:

(module
  (export foo)
  (local foo 10))
; => (do (local foo 10) {:foo foo})

I doubt if this is advised in the spirit of fennel though.

technomancy commented 4 months ago

I see what you mean. Yes, you could build that out, and it would work fine right until you try to do it from the repl. But it's better to use the Lua module system since it's much more transparent. There's only so much we can do to work around Lua's limitations with scope and chunks.