Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Macro question: Multiple value binding for local #433

Closed
alecStewart1 opened this issue Oct 12, 2022 · 5 comments
Closed

Macro question: Multiple value binding for local #433

alecStewart1 opened this issue Oct 12, 2022 · 5 comments

Comments

@alecStewart1
Copy link

I was wondering if it work possible to create a way to do a multiple bind for local by creating a macro. Say it would be:

(local!
  x 1
  y 2)

However, I'm not entirely sure how to do this, as you'd have to do:

(local!
  x 1 ; <- this is a key/value pair, but x is a symbol and not a key like :x or "x".
  y 2)

The initial way I thought you could do it would be:

(macro local! [...] ; <- has to be varadic
  (each [symbol value (ipairs [...])] ; both pairs or ipairs will produce the issue I'm having
    `(local symbol ,value)))

However symbol will not be a symbol if you pass (local! x 1), it will be a number. As when you call the macro after creating it in a REPL:

Compile error: Compile error in unknown:2:0
  [string "local function _1_(...)..."]:3: attempt to call a number value (local 'symbol')
stack traceback:
        [string "local function _1_(...)..."]:3: in function <[string "local function _1_(...)..."]:1>
        (...tail calls...)
        [C]: in function 'xpcall'
        /usr/local/bin/fennel:2959: in function 'fennel.compiler.macroexpand'
        /usr/local/bin/fennel:3146: in function 'fennel.compiler.compile1'
        /usr/local/bin/fennel:3443: in function 'fennel.compiler.compile'
        [C]: in function 'pcall'
        /usr/local/bin/fennel:851: in function </usr/local/bin/fennel:829>
        (...tail calls...)
        /usr/local/bin/fennel:894: in function 'fennel.repl'
        (...tail calls...)
        [C]: in ?

(local! x 1)

So how would one go about implementing this? symbol in the macro has to be a symbol and the name of that symbol is one that is passed.

@technomancy
Copy link
Collaborator

There's a few different problems with this. The first problem is that you're stepping thru the list one element at a time when you need to step two at a time. Also you're using each which is meant for side-effects, but in a macro you need to return a form instead. You could use icollect for this, but that would just return a table, which isn't really what you want either. You seem to be trying to emit multiple forms from a single macro, which isn't really something that macros can do; every macro call compiles to a single form.

The good news is you don't need a macro for this:

(local (x y) (values 1 2))

It's already supported with regular local.

Hope that helps.

@alecStewart1
Copy link
Author

alecStewart1 commented Oct 13, 2022

That works, but it's just a bit ugly once you get to n amount of local variables in one file, and what if you wanted something like let* in Common Lisp? Say have something odd like this (I don't really know why you would but it's just an example):

(local (mod mapcan arr modified-arr) 
    (values
      (require :mod)
      mod.mapcan
      [1 2 3 4]
      (mapcan arr (fn (n) (* n 2)))))

Again I don't really know why you'd have something like this with this example, mod, mapcan and arraren't know in the context of the value form.

@technomancy
Copy link
Collaborator

Sure, once you get beyond 2 or 3 values it's awkward, but at that point there's no longer any reason to do it on one form; you might as well spread it out into multiple locals since it's going to be more than one line anyway by then.

But if you really want to do it in a single form, you could write a macro that accepts your (local! x 1 y 2) notation and emits a single (local (x y) (values 1 2)) form. You don't care about the readability of the macroexpansion.

what if you wanted something like let* in Common Lisp?

The let form in Fennel already works like let* in CL.

@alecStewart1
Copy link
Author

Alright good to know! Thanks for the answers!

@technomancy
Copy link
Collaborator

Hey, I saw you reopened this; is there still an open question here that needs further clarification?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants