~technomancy/fennel#200: 
Unnecessary gensym when assigning to a newly introduced identifier

When you're introducing a new identifier, and it's being bound to an expression of the form (do ... x#) or (let [...] x#), the x# could be replaced with the new identifier being introduced. This would reduce the number of gensyms in generated code.

See the following example:

(local x
  (?. a b c)

fennel --complile test.fnl

#Current (as of Fennel 1.4.1-dev):

local x
do
  local t_1_ = a
  if (nil ~= t_1_) then
    t_1_ = t_1_[b]
  else
  end
  if (nil ~= t_1_) then
    t_1_ = t_1_[c]
  else
  end
  x = t_1_
end
return nil

#Ideal / Optimized

local x
do
  x = a
  if (nil ~= x) then
    x = x[b]
  else
  end
  if (nil ~= x) then
    x = x[c]
  else
  end
end
return nil

I suspect this would require a relatively large refactor of the compiler to accomplish.

This appears ~22 times in the fennel compendium, but I would like to believe it is undercounted as the fennel compendium games seem less likely to use macros like (icollect) and (doto), which are the main sources of this category of gensym:

(local x (doto (foo) (bar)))
(local x (icollect ...))
(local x (?. a b c d e)

I counted these using the regular expression ^ *[-a-z0-9?]+ = [a-z]*_[0-9]+_(auto)?\n *end, manually skipping places where the gensym is a function, as in cases like (local x (fn [...] ...).

Status
REPORTED
Submitter
~xerool
Assigned to
No-one
Submitted
1 year, 29 days ago
Updated
11 months ago
Labels
output

~technomancy 11 months ago

I don't understand how this would work. It seems like you're suggesting that macros should have access to see what target their return value is being assigned to? I could see this working with some specials, but with a macro it seems impossible. Maybe I'm misunderstanding.

~xerool 11 months ago*

In my head, I'm imagining something like:

  1. macros get expanded
  2. compiler is in SPECIALS.let
  3. let* notices "oh hey the thing that I return is a gensym that I declare"
  4. let* somehow verifies that the target is a new variable being declared, so that its safe to write dummy intermediate values to the target. In all of my examples, the target is a new variable being declared, so it is safe.
  5. let* tweaks the scope manglings so that assignments to the gensym point directly to the target.
  6. then, continue compilation
  7. at the end, skip emitting the assignment from the gensym to the target

The target in the lua blocks above would be x and the gensym is t_1_

~xerool 11 months ago*

So I'm proposing this only for the let (and maybe the do) special. I think all the mentioned macros expand to let.

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