0

I have LISP written in JavaScript (https://jcubic.github.io/lips/ with online demo where you can try it) and I have macro like this:

(define-macro (globalize symbol)
  (let ((obj (--> (. lips 'env) (get symbol))))
    `(begin
       ,@(map (lambda (key)
                (print (concat key " " (function? (. obj key))))
                (if (function? (. obj key))
                    (let* ((fname (gensym))
                           (args (gensym))
                           (code `(define (,(string->symbol key) . ,args)
                                    (apply (. ,obj ,key) ,args))))
                      (print code)
                      code)))
              ;; native Object.key function call on input object
              (array->list (--> Object (keys obj)))))))

In this code I use this:

(let ((obj (--> (. lips 'env) (get symbol))))

and I call this macro using:

(globalize pfs)

to create function for each static method of pfs (which is LightingFS from isomorphic-git where each function return a promise, it's like fs from node).

But it will not work for something like this:

(let ((x pfs))
  (globalize x))

because lips.env is global enviroment.

So my question is this how macro should work? Should they only process input data as symbols so they never have access to object before evaluation of lisp code?

How the LISP macro that generate bunch of functions based on variable should look like. For instance in scheme if I have alist in variable and want to generate function for each key that will return a value:

input:

(define input `((foo . 10) (bar . 20)))

output:

(begin
  (define (foo) 10)
  (define (bar) 20))

Can I write macro that will give such output if I use (macro input)? Or the only option is (macro ((foo . 10) (bar . 20)))?

I can accept generic Scheme or Common LISP answer but please don't post define-syntax and hygienic macros from scheme, My lisp don't have them and will never have.

The problem seems to be that I want to access value at macro expansion time and it need to have the value that in runtime. And second question Is eval in this case the only option?

This works in biwascheme:

(define-macro (macro obj)
  (let ((obj (eval obj)))
    `(begin
       ,@(map (lambda (pair)
                (let ((name (car pair))
                      (value (cdr pair)))
                `(define (,name) ,value)))
              obj))))

(define input `((foo . 10) (bar . 20)))

(macro input)

(foo)
;; ==> 10
(bar)
;; ==> 20

(in my lisp eval don't work like in biwascheme but that's other issue).

but this don't work, because x is not global:

(let ((x '((g . 10)))) (macro x))

Is macro with eval something you would normally do, or should them be avoided? Is there other way to generate bunch of functions based on runtime object.

jcubic
  • 61,973
  • 54
  • 229
  • 402
  • *"(..) how macro should work? Should they only process input data as symbols so they never have access to object before evaluation of lisp code?"* - **yes** (IMHO) – rsm Apr 25 '19 at 21:12
  • @rsm how then I should go about and create functions at runtime? – jcubic Apr 25 '19 at 21:15
  • `(macro x)` will do something with the symbol `x` before knowing what significance it has. Some implementations does macro expansion ahead of execution and doesn't have macros at all in runtime. These systems can be R5RS when `eval` was optional. The expansion should be possible to put in `(macro x)` place and it should work interchangeably. – Sylwester Apr 25 '19 at 23:54
  • 1
    If your macro needs a value at macroexpansion-time, then it needs to get that value from somewhere. There is no way around that. How the macro gets it, that can be via EVAL, asking a symbol for its value, retrieving it from some data structure, etc. All have in common: these values need to be retrievable at macroexpansion-time. – Rainer Joswig Apr 26 '19 at 05:24

1 Answers1

2

In Common Lisp: creating and compiling functions at runtime.

CL-USER 20 > (defparameter *input* '((foo . 10) (bar . 20)))
*INPUT*

CL-USER 21 > (defun make-my-functions (input)
               (loop for (symbol . number) in input
                     do (compile symbol `(lambda  () ,number))))
MAKE-MY-FUNCTIONS

CL-USER 22 > (make-my-functions *input*)
NIL

CL-USER 23 > (foo)
10

CL-USER 24 > (bar)
20

From a local variable:

CL-USER 25 > (let ((input '((foo2 . 102) (bar3 . 303))))
               (make-my-functions input))
NIL

CL-USER 26 > (bar3)
303

With a macro, more clumsy and limited:

CL-USER 37 > (defparameter *input* '((foo1 . 101) (bar2 . 202)))
*INPUT*

CL-USER 38 > (defmacro def-my-functions (input &optional getter)
               `(progn
                  ,@(loop for (symbol . number) in (if getter
                                                       (funcall getter input)
                                                       input)
                          collect `(defun ,symbol () ,number))))
DEF-MY-FUNCTIONS

CL-USER 39 > (def-my-functions *input* symbol-value)
BAR2

CL-USER 40 > (foo1)
101
Rainer Joswig
  • 136,269
  • 10
  • 221
  • 346
  • 1
    This works for simple case, but it seems that my simple case is not equal the complex one. In my original code I have list of names that I can get at runtime and object I call this on. Not sure but this is like passing CLOS object and list of generic functions and function or macro generate new normal function where generic is inside and it call on that CLOS object. And object can be in `let`. – jcubic Apr 26 '19 at 08:05
  • @jcubic: maybe you should explain what you actually want to do. Just posting code in your own Lisp dialect is probably not of much help, when we don't know what it does or should do. – Rainer Joswig Apr 26 '19 at 08:11
  • @jcubic: as I showed you in my example, Common Lisp can generate code and compile it - without macros - no matter what it calls and what the arguments are. Basic rule: if you want to do something at runtime, based on runtime data, use a function. – Rainer Joswig Apr 26 '19 at 08:18
  • So what I need is to have something like merge of two it seems incompatible concepts closures and macro expantion or a way to have it. `(let ((x ) (name 'foo)) '(define (,name) x))` before define there is back tick . so what i need to have function name at runtime and value from closure. Maybe I need different define function that generete new function when used inside let. in scheme this don't work (at least in biwascheme) you need `(define foo (let ((x 10)) (lambda () x))` – jcubic Apr 26 '19 at 08:39
  • It don't need to be a macro, I only need to generate bunch of functions that get value from closure and name at runtime. – jcubic Apr 26 '19 at 08:44
  • 1
    @jcubic: in Scheme one would use a plain EVAL, instead of CL's COMPILE. Maybe a specific Scheme implementation has the equivalent to it. – Rainer Joswig Apr 26 '19 at 10:52