c3d / xl

A minimalist, general-purpose programming language based on meta-programming and parse tree rewrites
GNU General Public License v3.0
270 stars 15 forks source link

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

Open Christopher-Chianelli opened 4 years ago

Christopher-Chianelli commented 4 years ago

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 commented 4 years ago

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:

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 commented 4 years ago

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

dumblob commented 3 years ago
  • 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 :wink:.