Infinite loop with same name #112

Closed
opened 2025-12-30 01:21:01 +01:00 by adam · 6 comments
Owner

Originally created by @jw-y on GitHub (Mar 14, 2024).

tmp.pkl

class Bar {
    foos: Listing<String>
}
foos = new {
    "hi"
} 
bar = new {
    foos = foos
}
pkl eval tmp.pkl

Eval doesn't finish while

tmp2.pkl

class Bar {
    foos: Listing<String>
}
foo = new {
    "hi"
} 
bar = new {
    foos = foo
}
pkl eval tmp2.pkl

Here eval outputs correctly.

I get that foos in rhs of foos=foos is referring to it it's own, Bar's foos,
therefore the infinite loop, but I think this is counter-intuitive

or is it duplicate of #117

Originally created by @jw-y on GitHub (Mar 14, 2024). tmp.pkl ``` class Bar { foos: Listing<String> } foos = new { "hi" } bar = new { foos = foos } ``` ```bash pkl eval tmp.pkl ``` Eval doesn't finish while tmp2.pkl ``` class Bar { foos: Listing<String> } foo = new { "hi" } bar = new { foos = foo } ``` ```bash pkl eval tmp2.pkl ``` Here eval outputs correctly. I get that `foos` in rhs of `foos=foos` is referring to it it's own, `Bar`'s `foos`, therefore the infinite loop, but I think this is counter-intuitive or is it duplicate of #117
adam closed this issue 2025-12-30 01:21:01 +01:00
Author
Owner

@StefMa commented on GitHub (Mar 14, 2024):

I guess it is "intuitive" 🤔
You create a new object by extending Bar.
In your new object you set the variable to the exact same variable. => Recursive.
If you want to use the one from your parent class, you can use super.foos.
And this works 🙃
See https://pkl-playground.vercel.app/?share=officer-bush-group

@StefMa commented on GitHub (Mar 14, 2024): I guess it is "intuitive" 🤔 You create a new object by extending `Bar`. In your new object you set the variable to the exact same variable. => Recursive. If you want to use the one from your parent class, you can use `super.foos`. And this works 🙃 See https://pkl-playground.vercel.app/?share=officer-bush-group
Author
Owner

@bioball commented on GitHub (Mar 14, 2024):

This is expected behavior (you can't define a property in terms of itself).

In order to reference the outer foo, you'll need to qualify that reference somehow so that Pkl knows that you're referring to a different foo.

In your case, you can either use module.foo, or outer.foo, because foo is defined on the module and it is one lexical scope higher.

In the case where it's not available off module, it's common to do something like this:


bar {
  local self = this
  foo = 1
  baz {
    biz {
      foo = self.foo
    }
  }
}
@bioball commented on GitHub (Mar 14, 2024): This is expected behavior (you can't define a property in terms of itself). In order to reference the outer `foo`, you'll need to qualify that reference somehow so that Pkl knows that you're referring to a _different_ foo. In your case, you can either use `module.foo`, or `outer.foo`, because `foo` is defined on the module _and_ it is one lexical scope higher. In the case where it's not available off `module`, it's common to do something like this: ```groovy bar { local self = this foo = 1 baz { biz { foo = self.foo } } } ```
Author
Owner

@jw-y commented on GitHub (Mar 15, 2024):

Thanks for the replies!
I was actually going for builder pattern in the following way

function BarBuilder(foo) = new Bar { foo = foo }

In most programming languages (if not all), you would expect foo on the rhs to refer to function Bird's argument.
This is "counter-intuitive" in terms of other programming languages.

I guess this boils down to pkl's two traits

  1. pkl doesn't need special keyword like this or self to refer to it's own member
  2. pkl gives precedence to its own members over outside local variables

I guess this is a quirk of pkl?

@jw-y commented on GitHub (Mar 15, 2024): Thanks for the replies! I was actually going for builder pattern in the following way ``` function BarBuilder(foo) = new Bar { foo = foo } ``` In most programming languages (if not all), you would expect `foo` on the rhs to refer to `function Bird`'s argument. This is "counter-intuitive" in terms of other programming languages. I guess this boils down to pkl's two traits 1. pkl doesn't need special keyword like `this` or `self` to refer to it's own member 2. pkl gives precedence to its own members over outside local variables I guess this is a quirk of pkl?
Author
Owner

@bioball commented on GitHub (Mar 15, 2024):

It's actually not due to implicit this; it's because foo is in the lexical scope.

Other languages that have similar scoping rules have similar quirks. For example, the following java is also defining variable name in terms of itself, instead of assigning to the outer name:

class Person {
  private final String name;

  public Person(String name) {
    name = name; 
  }
}

To fix, you need to qualify which name you're talking about:

   public Person(String name) {
-    name = name;
+    this.name = name;
   }

Also, BTW: the builder pattern generally isn't that useful in Pkl, because Pkl objects already fulfill the role of a "builder".

Consider:

class Person {
  name: String
  age: Int
}

function Person(_name: String, _age: Int): Person = new { name = _name; age = _age }

p1: Person = new {
  name = "Susan"
  age = 31
}

p2: Person = Person("Susan", 31)

Both p1 and p2 are the same thing, but p1 is more readable, and it's also clearer what 31 is in the first example because it's named.

@bioball commented on GitHub (Mar 15, 2024): It's actually not due to implicit `this`; it's because `foo` is in the lexical scope. Other languages that have similar scoping rules have similar quirks. For example, the following java is also defining variable `name` in terms of itself, instead of assigning to the outer `name`: ```java class Person { private final String name; public Person(String name) { name = name; } } ``` To fix, you need to qualify _which_ `name` you're talking about: ```diff public Person(String name) { - name = name; + this.name = name; } ``` Also, BTW: the builder pattern generally isn't that useful in Pkl, because Pkl objects already fulfill the role of a "builder". Consider: ```groovy class Person { name: String age: Int } function Person(_name: String, _age: Int): Person = new { name = _name; age = _age } p1: Person = new { name = "Susan" age = 31 } p2: Person = Person("Susan", 31) ``` Both `p1` and `p2` are the same thing, but `p1` is more readable, and it's also clearer what `31` is in the first example because it's named.
Author
Owner

@bioball commented on GitHub (Mar 15, 2024):

Closing this as an issue, but feel free to keep commenting if you want to learn more about scoping or coding style!

@bioball commented on GitHub (Mar 15, 2024): Closing this as an issue, but feel free to keep commenting if you want to learn more about scoping or coding style!
Author
Owner

@jw-y commented on GitHub (Mar 17, 2024):

Got it! Thanks for the detailed reply!

@jw-y commented on GitHub (Mar 17, 2024): Got it! Thanks for the detailed reply!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/pkl#112