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

'PRINT is a valid function name #66

Closed
dragoncoder047 opened this issue Jun 17, 2024 · 15 comments
Closed

'PRINT is a valid function name #66

dragoncoder047 opened this issue Jun 17, 2024 · 15 comments

Comments

@dragoncoder047
Copy link

In Common Lisp:

CL-USER> (progn ('print 'foo) (values))
Error: 'PRINT is not a function name; try using a symbol instead

But in uLisp:

> (progn ('print 'foo) nothing)
foo

Going to have to think about how to fix this.

@technoblogy
Copy link
Owner

Going to have to think about how to fix this.

I don't think it's necessary (or useful) to fix it. I think it's a consequence of uLisp being a Lisp-1, and Common Lisp being a Lisp-2.

@dragoncoder047
Copy link
Author

Perhaps that bit doesn't need to be fixed, but here's another weirdness that occurs when you use a quoted symbol as the function:

uLisp:

> ('progn '('print 'foo) ('print 'bar))
bar
foo
bar

(At least in my version of uLisp, the TAILCALL flag may be changing the behavior here. Try that on stock 4.6, you might get something different.)

CL gives the same "not a function name" error with that.

After some thought I think at least the first example can be corrected by something like what I mentioned in this comment from a year ago. I'll try it and see if it works. I have no clue how to fix the second one.

@technoblogy
Copy link
Owner

It does the same on Version 4.6, and I can understand why, but I really don't think it's worth spending any time trying to "fix" it.

@technoblogy
Copy link
Owner

I've found a fix. In eval() change:

  // Evaluate the parameters - result in head
  object *fname = car(form);
  int TCstart = TC;
  object *head = cons(eval(fname, env), NULL);
  ...

to

  // Evaluate the parameters - result in head
  object *fname = car(form);
  int TCstart = TC;
  object *head;  
  if (symbolp(fname) && !builtinp(fname->name)) head = cons(eval(fname, env), NULL);
  else head = cons(fname, NULL);
  ...

It avoids evaluating the first item in a list if it's a built-in function.

Your tests:

> ('print 12)
Error: illegal function: (quote print)

> (progn ('print 'foo) nothing)
Error: 'quote' illegal function: (quote print)

> ('progn '('print 'foo) ('print 'bar))
Error: 'quote' illegal function: (quote print)

It passes the test suite on ARM; I need to test the other platforms.

Although as far as I know it has had no impact on users, it is definitely worth fixing because it significantly speeds up uLisp; for example:

Tak on an ATSAMD21 on ARM Version 4.6, : 14.0 s
Tak on an ATSAMD21 with this modification: 10.4 s

So great work finding that!

@dragoncoder047
Copy link
Author

There's one thing that your fix doesn't cover:

> (progn 'foo)
foo
> ('progn 'foo)
Error: undefined: foo

I just made some changes that also fix the 'print problem, but it doesn't cover special forms either. The only advantage is this:

> print
<built-in function print>
> progn
<built-in special form progn>

As opposed to what stock 4.6 does:

> print
print
> progn
progn

@technoblogy
Copy link
Owner

Not sure this would be more useful than uLisp's current behaviour.

@dragoncoder047
Copy link
Author

At the very least, the bug where if you quote ('progn) it evaluates some of the forms twice, is going to have to be fixed. (on my fork it's sorta fixed, it evaluates the forms and then complains "can't call a symbol", but it should start off with an error.)

@technoblogy
Copy link
Owner

Thanks for these comments. I've decided to incorporate some of the changes you've made to eval() to fix these in an interim release, and also try to fix your previous point, which I assume is referring to:

> ('b a b)
Error: undefined: a

where the error should really be:

Error: can't call a symbol: b

One thing I don't quite understand in your fork. In this section of eval():

    // fail early on calling a symbol
    if (symbolp(function)) {
        Context = NIL;
        error("can't call a symbol", function); 
    }
    if (bfunctionp(function)) {
        builtin_t bname = builtin(function->name);
        if (!builtinp(function->name)) error("can't call a symbol", function);

under what conditions will the second "can't call a symbol" error message get triggered?

@dragoncoder047
Copy link
Author

I just ran the entire test suite with the second "can't call a symbol" message rigged to crash my ESP32 instead, and it didn't crash. So I think it's pretty well unreachable. I guess I forgot to remove it from something else.

One thing that does happen in the test suite now is stuff like this:

> (aeq 'funcall 4 (let ((x2 (lambda (fun) (lambda (x) (fun (fun x)))))) ((x2 '1+) 2)))
Error: can't call a symbol: 1+

because the 1+ is being passed in quoted, and this "transform to a builtin" action isn't allowed to happen.

But anyway, that test isn't valid Common Lisp anyway:

CL-USER> (let ((x2 (lambda (fun) (lambda (x) (fun (fun x)))))) ((x2 '1+) 2))
Error: (X2 '1+) is not a function name; try using a symbol instead

I can't figure out the right combination of funcall and #' to make that work in Common Lisp, so I guess this is a feature that makes uLisp a little bit more like Scheme, I guess.

@dragoncoder047
Copy link
Author

Thanks for these comments. I've decided to incorporate some of the changes you've made to eval() to fix these in an interim release, and also try to fix your previous point, which I assume is referring to:

> ('b a b)
Error: undefined: a

Specifically, I was referring to the fact that if it is a special form (such as progn) that is the function being quoted, then the arguments that are normally passed in unevaluated, get evaluated before they get passed in, the special form doesn't know that's been done, and in the case of progn it evaluates them a second time (so you wrote 'a which evaluates to a which evaluates to Error: undefined: a).

@technoblogy
Copy link
Owner

One thing that does happen in the test suite now is stuff like this:

Yes, there are a couple of tests in the test suite that don't make much sense, and only work in uLisp by accident, so I'm going to remove them. I'll try and find something similar to replace them with.

I was referring to the fact that if it is a special form (such as progn) that is the function being quoted, then the arguments that are normally passed in unevaluated, get evaluated before they get passed in

So you're referring to this example?

('progn '('print 'foo) ('print 'bar))
Error: can't call a symbol: print

What should the error be in this case? Common Lisp effectively gives:

Illegal car (quote progn)

@dragoncoder047
Copy link
Author

So you're referring to this example?

('progn '('print 'foo) ('print 'bar))
Error: can't call a symbol: print

What should the error be in this case? Common Lisp effectively gives:

Illegal car (quote progn)

I think the best error in that case would be Error: can't call a symbol: progn but I can't figure out how to test for that case in the case of special forms. My fork currently is able to detect that but I'm not sure how, since the eval() algorithm is still a little strange.

@dragoncoder047
Copy link
Author

On further investigation there is still a problem:

> ('progn (print 'a))
a 
Error: can't call a symbol: progn

(the a should never be printed.)

@technoblogy
Copy link
Owner

Yes, thanks, I'll fix that.

@technoblogy
Copy link
Owner

I've tried to fix most of these issues in Release 4.6b of the ESP Version of uLisp, and they'll be fixed in the other versions in due course - thanks for finding them!

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