STklos icon indicating copy to clipboard operation
STklos copied to clipboard

symbol '%find-macro-clause' unbound with users SRFI-8

Open Retropikzel opened this issue 1 year ago • 11 comments

main.scm:

(import (scheme base)
        (scheme write)
        (srfi 8))

(receive (a b) (values 1 2)
         (begin
           (display a)
           (newline)
           (display b)
           (newline)))

srfi/8.sld:

(define-library
  (srfi 8)
  (import (scheme base))
  (export receive)
  (include "8.scm"))

srfi/8.scm:

(define-syntax receive
  (syntax-rules ()
    ((receive formals expression body ...)
     (call-with-values (lambda () expression)
                       (lambda formals body ...)))))

stklos -I . main.scm gives error:

**** Error while executing command ("main.scm")
         Where: in compile
        Reason: symbol '%find-macro-clause' unbound in module 'stklos'

  - >
  - #[closure 7fa63a2f8f00]
  - >
  - compile
  - #[closure 7fa63a2f8140]
  - >
  - #[closure 7fa63a2f80c0]
  - %call-for-values
  - >
  - call-with-values
  - ...
Set shell variable STKLOS_FRAMES to set visible frames
EXIT

If you comment out the srfi 8 import and copy the code directly to the file it works.

Retropikzel avatar Dec 24 '24 09:12 Retropikzel

Hello @Retropikzel

Indeed. The %find-macro-clause procedure is inside module stklos, but libraries won't load it. STklos module sdo automatically load the stklos module, so this:

(define-module
  srfi/8
  (import (scheme base))
  (export receive)
  (include "8.scm"))

works.

Of course, the library definition should work...

You can also (import stklos) in the library definition, it will then work.

The root of the problem seems to be issue #703 : local symbols which is used by exported syntax is not really exported. This seems to be what happened here...

@egallesio is this something easy to fix?

jpellegrini avatar Dec 26 '24 20:12 jpellegrini

Hi @Retropikzel,

Thanks for signaling this issue. I will try to correct this rapidly (but not probably before next week).

@egallesio is this something easy to fix?

This particular issue is probably easy to correct, but I suppose it will be more difficult to have a correct behavior with the problem you exposed in #703. More on this soon, I hope.

egallesio avatar Dec 29 '24 17:12 egallesio

Hi,

Last commit corrects this issue.

Happy new year.

egallesio avatar Jan 04 '25 10:01 egallesio

@Retropikzel it seems that your original code now works! :)

jpellegrini avatar Jan 04 '25 11:01 jpellegrini

@egallesio I'll review old libraries and SRFIs and see if we ca nremove some of the "(export %find-macro-clause)) from them.

jpellegrini avatar Jan 04 '25 11:01 jpellegrini

Thank you very much! :)

Retropikzel avatar Jan 04 '25 11:01 Retropikzel

@egallesio I'll review old libraries and SRFIs and see if we ca nremove some of the "(export %find-macro-clause)) from them.

Normally, there is no more occurrence of %find-macro-clause in the source tree. Thanks @jpellegrini

egallesio avatar Jan 04 '25 18:01 egallesio

I think this might also be similar.

main.scm:

(import (scheme base)
        (scheme write)
        (foo))

(display "The value of bar is: ")
(display bar)
(newline)

foo.sld:

(define-library
  (foo)
  (import (scheme base))
  (export bar)
  (begin
    (define bar
      (guard (error
               (else "bar"))
        (vector-ref (vector) 100)))))
 

stklos main.scm:

 **** Error while executing command ("main.scm")
         Where: in %execute
        Reason: symbol 'current-exception-handler' unbound in library (foo)
 

stklos -V git stuff:

 (build.git.branch "master")
 (build.git.commit "834eae3")

Workaround foo.scm:

(define-library
  (foo)
  (import (scheme base))
  (export bar)
  (begin
    (define bar
      (call-with-current-continuation
        (lambda (k)
          (with-exception-handler
            (lambda (x) (k "bar"))
            (lambda () (vector-ref (vector) 100))))))))
 

I also tested foo.scm:

 (define-library
  (foo)
  (import (scheme base))
  (export bar)
  (begin
    (define bar
      (with-exception-handler
        (lambda (x) "bar")
        (lambda () (vector-ref (vector) 100))))))

And got:

**** Error while executing command ("main.scm")
         Where: in with-exception-handler
        Reason: exception handler returned on non-continuable exception
 

I have no idea what the last one should do, but I thought I'd test it to be more thorough.

Retropikzel avatar Jan 05 '25 06:01 Retropikzel

@egallesio I think this has to do with #410 . Macros are created but they do not remember the environment in which they are created -- this is why macros created in modules have such problem:

(let ((x 10))
  (let-syntax ((a (syntax-rules () ((_) x))))
    (let ((x 20))
      (a))))

STklos answers 20, but it should be 10.

So when a macro is created inside a module, there is a similar problem. It doesn't remember the module definitions (which it should, without need for importing)...

If there was a way to get code compiled within a module, then it would be possible to get the macro system to remember the bindings (we'd just compile the macro in the proper module).

Or did I get something wrong?

jpellegrini avatar Jan 05 '25 16:01 jpellegrini

I have applied a quick fix for this problem, but I'll need to modify the implementation of guard, since it's behavior with multiple values is incorrect.

About the code

(with-exception-handler
        (lambda (x) "bar")
        (lambda () (vector-ref (vector) 100)))

The error about an exception handler returned on non-continuable exception is normal. This is the R7RS expected behavior. In raise it is said that

If the handler returns, a secondary exception is raised in the same dynamic environment as the handler.

All the other scheme systems I have tested also detect an error in this case.

egallesio avatar Jan 08 '25 14:01 egallesio

STklos answers 20, but it should be 10

Yes, this is a known problem (See https://github.com/egallesio/STklos/pull/239#issuecomment-1163217488). I hope to change that in not so far future. For now, don't forget that let-syntax is a joke :smiling_face_with_tear: .

If there was a way to get code compiled within a module, then it would be possible to get the macro system to remember the bindings (we'd just compile the macro in the proper module).

This is another problem. In fact, the complexity here comes from the fact that when we compile a file, which imports a library, we do not load at all the code of the library, since it can have undesirable side effects (displays, system interactions, ...) So the compiler reads the metadata of the .ostk file which contain the expander of the macro. If this expander uses some functions or non-exported macros, it cannot find them in the metadata. Things could be simpler, if we don't use compile-file, since imported code will be executed (but it would imply that we could have a different behavior depending on we use compile-file or not).

With pure R7RS libraries, we could imagine the loading of only the definitions parts of the library (that is, everything except the begin part) and rebuild the environment of the imported library. However, this is not OK with STklos modules, where definitions and code can be interleaved. For now, I have not an easy solution for this problem.

Hope that I was clear in my explanation.

egallesio avatar Jan 08 '25 14:01 egallesio

This is another problem. In fact, the complexity here comes from the fact that when we compile a file, which imports a library, we do not load at all the code of the library, since it can have undesirable side effects (displays, system interactions, ...) So the compiler reads the metadata of the .ostk file which contain the expander of the macro. If this expander uses some functions or non-exported macros, it cannot find them in the metadata. Things could be simpler, if we don't use compile-file, since imported code will be executed (but it would imply that we could have a different behavior depending on we use compile-file or not).

@egallesio I was thinking about this, and I would see three options:

  1. Disable I/O, disable external assignments (in fact, disable all side effects except assigning to variables in the module), load the module, and enable them again
  2. Disable I/O only (since the user would expect the assignments to work) while loading
  3. Just let all side-effects happen (principle of least surprise -- the user coded that, so the expectation is that those things will be executed...)

What do you think?

jpellegrini avatar Mar 19 '25 10:03 jpellegrini

If there was a way to get code compiled within a module, then it would be possible to get the macro system to remember the bindings (we'd just compile the macro in the proper module).

This is another problem. In fact, the complexity here comes from the fact that when we compile a file, which imports a library, we do not load at all the code of the library, since it can have undesirable side effects (displays, system interactions, ...) So the compiler reads the metadata of the .ostk file which contain the expander of the macro. If this expander uses some functions or non-exported macros, it cannot find them in the metadata. Things could be simpler, if we don't use compile-file, since imported code will be executed (but it would imply that we could have a different behavior depending on we use compile-file or not).

With pure R7RS libraries, we could imagine the loading of only the definitions parts of the library (that is, everything except the begin part) and rebuild the environment of the imported library. However, this is not OK with STklos modules, where definitions and code can be interleaved. For now, I have not an easy solution for this problem.

Hope that I was clear in my explanation.

Maybe the implementation of the idea in issue #754 would help with this?

jpellegrini avatar Jul 29 '25 02:07 jpellegrini