default values not being used in mapping when explicitly calling new on Object #129

Open
opened 2025-12-30 01:21:16 +01:00 by adam · 5 comments
Owner

Originally created by @harryjackson on GitHub (Mar 30, 2024).

I created a Mapping with some default values for the object values and in some cases the values get used and in others they don't.

I've created an example of what I mean here. The code as is should work if you run

pkl eval defaults.pkl

Note the differences between lines 57 and 61, now comment out line 62 and "pkl eval defaults.pkl" fails. Can someone explain the reason the explicit call to "new Service.ServicePort" means the defaults are no longer used?

 57       [8086] = new {
 58         port = 8086
 59         targetPort = port
 60       }
 61       [8081] = new Service.ServicePort {
 62         //name = "debug"
 63         port = 8081
 64         targetPort = port
 65       }
Originally created by @harryjackson on GitHub (Mar 30, 2024). I created a Mapping with some default values for the object values and in some cases the values get used and in others they don't. I've created an [example of what I mean here](https://github.com/harryjackson/pkl-example/blob/main/defaults.pkl). The code as is should work if you run ```bash pkl eval defaults.pkl ``` Note the differences between lines 57 and 61, now comment out line 62 and "pkl eval defaults.pkl" fails. Can someone explain the reason the explicit call to "new Service.ServicePort" means the defaults are no longer used? ```bash 57 [8086] = new { 58 port = 8086 59 targetPort = port 60 } 61 [8081] = new Service.ServicePort { 62 //name = "debug" 63 port = 8081 64 targetPort = port 65 } ```
Author
Owner

@HT154 commented on GitHub (Apr 2, 2024):

This comes down to what is being amended. There's actually yet another option here that looks like this:

  [8088] {
    port = 8088
    targetPort = port
  }

Here's what each of these idioms means in (more) plain language:

  • direct amend (my above example) - amend the parent value
  • = new { - amend the default value as defined in the Mapping/Listing/class
  • = new Service.ServicePort { - creates a full new Service.ServicePort (amending only that class's default values)

It's a little easier to see it in action, here's a more complete example:

class MyThing {
  favoriteColors: Listing<String> = new {
    "blue"
  }
}

directAmend: MyThing = new { // this `new` makes a MyThing and amends the class's defaults
  favoriteColors { // this amend adds to the parent (the new MyThing) and its defaults
    "red"
  }
}

directAmendAgain: MyThing = (directAmend) { // this amends `directAmend`
  favoriteColors { // this amend adds to the parent (`directAmend`) and its value
    "yellow"
  }
}

defaultAmend: MyThing = (directAmend) {
  favoriteColors = new { // this amends the default value of `MyThing`'s `favoriteColors`
    "yellow"
  }
}

noAmend: MyThing = (directAmend) {
  favoriteColors = new Listing<String> { // this amends the default value of `Listing<String> ` (which is empty)
    "yellow"
  }
}

This evaluates to:

directAmend {
  favoriteColors { // the class's defaults, adding "red"
    "blue"
    "red"
  }
}
directAmendAgain {
  favoriteColors { // directAmend's value, adding "yellow"
    "blue"
    "red"
    "yellow"
  }
}
defaultAmend {
  favoriteColors { // the class's defaults, adding "yellow"
    "blue"
    "yellow"
  }
}
noAmend {
  favoriteColors { // an empty listing, adding "yellow"
    "yellow"
  }
}

Using default in a Mapping or Listing works similarly to the class default value I used in this example:

someThings: Listing<MyThing> = new {
  default {
    favoriteColors { // this amends the default value of `MyThing`'s `favoriteColors`
      "green"
    }
  }

  new { // this amends `someThings`'s `default`
    favoriteColors {
      "purple"
    }
  }
  new MyThing { // this `new` makes a MyThing and amends the class's defaults (ignoring the Listing's `default`)
    favoriteColors {
      "purple"
    }
  }
  new { // this amends `someThings`'s `default`
    favoriteColors = new { // but this amend's the class's default value (ignoring the Listing's `default`)
      "purple"
    }
  }
  new MyThing { // this `new` makes a MyThing and amends the class's defaults (ignoring the Listing's `default`)
    favoriteColors = new { // and this amend's the class's default value
      "purple"
    }
  }
  new { // this amends `someThings`'s `default`
    favoriteColors = new Listing<String> { // but this creates a new empty Listing and amends it
      "purple"
    }
  }
  new MyThing { // this `new` makes a MyThing and amends the class's defaults (ignoring the Listing's `default`)
    favoriteColors = new Listing<String> { // but this creates a new empty Listing and amends it
      "purple"
    }
  }
}

This evaluates to:

someThings {
  new {
    favoriteColors {
      "blue"
      "green"
      "purple"
    }
  }
  new {
    favoriteColors {
      "blue"
      "purple"
    }
  }
  new {
    favoriteColors {
      "blue"
      "purple"
    }
  }
  new {
    favoriteColors {
      "blue"
      "purple"
    }
  }
  new {
    favoriteColors {
      "purple"
    }
  }
  new {
    favoriteColors {
      "purple"
    }
  }
}
@HT154 commented on GitHub (Apr 2, 2024): This comes down to what is being amended. There's actually yet another option here that looks like this: ```pkl [8088] { port = 8088 targetPort = port } ``` Here's what each of these idioms means in (more) plain language: * direct amend (my above example) - amend the parent value * `= new {` - amend the default value as defined in the Mapping/Listing/class * `= new Service.ServicePort {` - creates a full new `Service.ServicePort` (amending only that class's default values) It's a little easier to see it in action, here's a more complete example: ```pkl class MyThing { favoriteColors: Listing<String> = new { "blue" } } directAmend: MyThing = new { // this `new` makes a MyThing and amends the class's defaults favoriteColors { // this amend adds to the parent (the new MyThing) and its defaults "red" } } directAmendAgain: MyThing = (directAmend) { // this amends `directAmend` favoriteColors { // this amend adds to the parent (`directAmend`) and its value "yellow" } } defaultAmend: MyThing = (directAmend) { favoriteColors = new { // this amends the default value of `MyThing`'s `favoriteColors` "yellow" } } noAmend: MyThing = (directAmend) { favoriteColors = new Listing<String> { // this amends the default value of `Listing<String> ` (which is empty) "yellow" } } ``` This evaluates to: ```pkl directAmend { favoriteColors { // the class's defaults, adding "red" "blue" "red" } } directAmendAgain { favoriteColors { // directAmend's value, adding "yellow" "blue" "red" "yellow" } } defaultAmend { favoriteColors { // the class's defaults, adding "yellow" "blue" "yellow" } } noAmend { favoriteColors { // an empty listing, adding "yellow" "yellow" } } ``` Using `default` in a Mapping or Listing works similarly to the class default value I used in this example: ```pkl someThings: Listing<MyThing> = new { default { favoriteColors { // this amends the default value of `MyThing`'s `favoriteColors` "green" } } new { // this amends `someThings`'s `default` favoriteColors { "purple" } } new MyThing { // this `new` makes a MyThing and amends the class's defaults (ignoring the Listing's `default`) favoriteColors { "purple" } } new { // this amends `someThings`'s `default` favoriteColors = new { // but this amend's the class's default value (ignoring the Listing's `default`) "purple" } } new MyThing { // this `new` makes a MyThing and amends the class's defaults (ignoring the Listing's `default`) favoriteColors = new { // and this amend's the class's default value "purple" } } new { // this amends `someThings`'s `default` favoriteColors = new Listing<String> { // but this creates a new empty Listing and amends it "purple" } } new MyThing { // this `new` makes a MyThing and amends the class's defaults (ignoring the Listing's `default`) favoriteColors = new Listing<String> { // but this creates a new empty Listing and amends it "purple" } } } ``` This evaluates to: ```pkl someThings { new { favoriteColors { "blue" "green" "purple" } } new { favoriteColors { "blue" "purple" } } new { favoriteColors { "blue" "purple" } } new { favoriteColors { "blue" "purple" } } new { favoriteColors { "purple" } } new { favoriteColors { "purple" } } } ```
Author
Owner

@holzensp commented on GitHub (Apr 3, 2024):

Totally what @HT154 said. Also, looking at your code, I'd suggest substituting lines 50-65 with

      default { _port ->
        appProtocol = "HTTP"
        name = appProtocol.toLowerCase()
        protocol = "TCP"
        port = _port
        targetPort = port
      }
      [8086] {}
      [8081] {
        name = "debug"
      }
  1. You don't need to repeat all the targetPort = port; that's unchanged from the default
  2. The type of Mapping<K,V>.default is (K) -> V; it's a function, so you can use the value of the key to express the default value of the entry.

Also, the property-by-property copy (lines 28-34) confuses me. Do you want everything as in sp, except the nodePort?

@holzensp commented on GitHub (Apr 3, 2024): Totally what @HT154 said. Also, looking at your code, I'd suggest substituting lines 50-65 with ``` default { _port -> appProtocol = "HTTP" name = appProtocol.toLowerCase() protocol = "TCP" port = _port targetPort = port } [8086] {} [8081] { name = "debug" } ``` 1. You don't need to repeat all the `targetPort = port`; that's unchanged from the default 2. The type of `Mapping<K,V>.default` is `(K) -> V`; it's a _function_, so you can use the value of the key to express the default value of the entry. Also, the property-by-property copy (lines 28-34) confuses me. Do you want everything as in `sp`, except the `nodePort`?
Author
Owner

@harryjackson commented on GitHub (Apr 6, 2024):

@holzensp Another lovely feature that I'm using a lot now.

@HT154 You need to write that up in the documentation somewhere. I can see how each of the options could be used as a feature and your description is great.

@harryjackson commented on GitHub (Apr 6, 2024): @holzensp Another lovely feature that I'm using a lot now. @HT154 You need to write that up in the documentation somewhere. I can see how each of the options could be used as a feature and your description is great.
Author
Owner

@HT154 commented on GitHub (Apr 9, 2024):

I'd love to get this into the docs! I'll defer to @holzensp and crew on where the best place to put it is. Maybe a new section under the language reference's Advanced Topics?

@HT154 commented on GitHub (Apr 9, 2024): I'd love to get this into the docs! I'll defer to @holzensp and crew on where the best place to put it is. Maybe a new section under the language reference's Advanced Topics?
Author
Owner

@HT154 commented on GitHub (Oct 15, 2024):

Details on this behavior were added to the language reference in https://github.com/apple/pkl/pull/624

@HT154 commented on GitHub (Oct 15, 2024): Details on this behavior were added to the language reference in https://github.com/apple/pkl/pull/624
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: starred/pkl#129