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

The factorial example in the README does not work as expected #4

Open
Christopher-Chianelli opened this issue Jan 26, 2020 · 3 comments
Labels

Comments

@Christopher-Chianelli
Copy link

The README shows the following code for a program that compute the factorial of 1 to 5:

0! is 1
N! is N * (N-1)!

for I in 1..5 loop
    print "The factorial of ", I, " is ", I!

I expect this to print:

The factorial of 1 is 1
The factorial of 2 is 2
The factorial of 3 is 6
The factorial of 4 is 24
The factorial of 5 is 120

However, the following is printed instead:

The factorial of I is N * (N - 1)!
The factorial of I is N * (N - 1)!
The factorial of I is N * (N - 1)!
The factorial of I is N * (N - 1)!
false

Environment: gcc:latest Docker image (Debian)
Occurs in both interpreted (xl -i file.xl) and compiled (xl file.xl) forms.

@c3d
Copy link
Owner

c3d commented Jan 26, 2020

Hi Christopher,

Thank you. You are correct. The binding of the variable in for loop is presently broken, and unfortunately, is likely to remain so for a few months 😦. To illustrate, the following currently works (at least in interpreted mode):

I := 6
print "The factorial of ", I, " is ", I!

But with a for loop, it does not work (yet, or no longer, depending on how you look at it).

For the longest of time, for loops have been a problem in the language because one of their arguments is created. In your example, a variable named I needs to be created, and if you define your own for loop, the scope of that variable used to be very unclear. The current definition, found here, creates a local variable Var that does not modify the incoming parameter. It currently looks like this:

for Var in Low..High loop Body is
    Var := Low
    while Var < High loop
        Body
        Var := Var + 1

That's very naive for a number of reasons One of them is Var := Var + 1, which only works for integer types. In your case, that's not a problem, but for I in 'A'..'C' would be broken by that. But there is nothing in that code that explains how Var would be anything but a local parameter. Which it presently is.

Earlier versions of the code have been working around this by special-casing it in the compiler (e.g. was the case in Tao3D), or trying to have a special annotation to the parameter (e.g. I:var integer, etc), which I was never happy with. All these variants broke under some common scenario. For example, you want Var to be visible in the while conditions, but to be visible in Body under some other name, I in your case.

I believe I finally found the "right" way to express this, which is documented here, where the code will look like this:

for N:name in R:[range of discrete] loop Body is 
    loop_context is 
        [[N]] : R.type := R.first 
    LoopVar is loop_context.[[N]] 
    while LoopVar <= R.last loop
        (loop_context) (Body) 
        ++LoopVar 

Here are the things that are broken for this to work:

  • Binding of the name type to preclude evaluation of the associated name
  • range of discrete is not available yet, because I just introduced (in the documentation only) the discrete type, and a generic range was never implemented since the XL2 times (circa 2005-ish)
  • [[N]] (a meta box in a declaration context to inject a name) is not implemented at all
  • I'm not entirely sure about the LoopVar declaration yet (whether you can modify through it). There are pros and cons.
  • The notation (loop_context) (Body) is supposed to be a scope injection of the loop_context scope while evaluating Body. While some support for this kind of prefix scoping operator made it to the interpreted version about one year ago, it's insufficient and broken in other cases.

So this explains why I believe it will be a while until the for loop actually works as intended.

Thanks a lot for reporting it, though. I will keep the issue open, and if/when I finally get the for loop to do the right thing while being written the right way, which will be a big victory, you will know 😄 .

@c3d
Copy link
Owner

c3d commented Jan 26, 2020

In order to show what some earlier implementations of the for loop used to look like, see this commit

@dumblob
Copy link

dumblob commented Jun 11, 2021

  • I'm not entirely sure about the LoopVar declaration yet (whether you can modify through it). There are pros and cons.

This can have something to do with immutability. Feel free to look at V lang (doc with examples) as a modern take on that (I like the "immutable by default" which I'm not sure XL could easily express (I have a feeling "mutable by default" should be doable in XL, but that partially defeats the added value of immutability). On the other hand I'm not exactly sure whether immutability is that useful in non-imperative style of programming.

So if I understand the problem with LoopVar correctly then the pros and cons you're mentioning seem to be rather something XL should factor out and let the programmer choose instead of letting the standard library (or "the language" if you want) decide it up front.

Just my 2 cents 😉.

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

No branches or pull requests

3 participants