[docs] Add documentation for new keyword (#624)

This commit is contained in:
Josh B
2024-08-29 15:25:14 -07:00
committed by GitHub
parent a8f24c9f13
commit 7001a42149

View File

@@ -3177,6 +3177,7 @@ pkl: TRACE: num1 * num2 = 672 (at file:///some/module.pkl, line 42)
This section discusses language features that are generally more relevant to template and library authors than template consumers.
<<meaning-of-new,Meaning of `new`>> +
<<let-expressions,Let Expressions>> +
<<type-tests,Type Tests>> +
<<type-casts,Type Casts>> +
@@ -3200,6 +3201,162 @@ This section discusses language features that are generally more relevant to tem
<<blank-identifiers,Blank Identifiers>> +
<<projects,Projects>>
[[meaning-of-new]]
=== Meaning of `new`
Objects in Pkl always <<amending-objects, amends>> _some_ value.
The `new` keyword is a special case of amending where a contextual value is amended.
In Pkl, there are two forms of `new` objects:
* `new` with explicit type information, for example, `new Foo {}`.
* `new` without type information, for example, `new {}`.
==== Type defaults
To understand instantiation cases without explicit parent or type information, it's important to first understand implicit default values.
When a property is declared in a module or class but is not provided an explicit default value, the property's default value becomes the type's default value.
Similarly, when `Listing` and `Mapping` types are declared with explicit type arguments for their element or value, their `default` property amends that declared type.
When `Listing` and `Mapping` types are declared without type arguments, their `default` property amends an empty `Dynamic` object.
Some types, including `Pair` and primitives like `String`, `Number`, and `Boolean` have no default value; attempting to render such a property results in the error "Tried to read property `<name>` but its value is undefined".
[source,{pkl}]
----
class Bird {
name: String = "polly"
}
bird: Bird // <1>
birdListing: Listing<Bird> // <2>
birdMapping: Mapping<String, Bird> // <3>
----
<1> Without an explicit default value, this property has default value `new Bird { name = "polly" }`
<2> With an explicit element type argument, this property's default value is equivalent to `new Listing<Bird> { default = (_) -> new Bird { name = "polly" } }`
<3> With an explicit value type argument, this property's default value is equivalent to `new Mapping<String, Bird> { default = (_) -> new Bird { name = "polly" } }`
==== Explicitly Typed `new`
Instantiating an object with `new <type>` results in a value that amends the specified type's default value.
Notably, creating a `Listing` element or assigning a `Mapping` entry value with an explicitly typed `new` ignores the object's `default` value.
[source,{pkl}]
----
class Bird {
/// The name of the bird
name: String
/// Whether this is a bird of prey or not.
isPredatory: Boolean?
}
newProperty = new Bird { // <1>
name = "Warbler"
}
someListing = new Listing<Bird> {
default {
isPredatory = true
}
new Bird { // <2>
name = "Sand Piper"
}
}
someMapping = new Mapping<String, Bird> {
default {
isPredatory = true
}
["Penguin"] = new Bird { // <3>
name = "Penguin"
}
}
----
<1> Assigning a `new` explicitly-typed value to a property.
<2> Adding an `new` explicitly-typed `Listing` element.
The value will not have property `isPredatory = true` as the `default` property of the `Listing` is not used.
<3> Assigning a `new` explicitly-typed value to a `Mapping` entry.
The value will not have property `isPredatory = true` as the `default` property of the `Mapping` is not used.
==== Implicitly Typed `new`
When using the implicitly typed `new` invocation, there is no explicit parent value to amend.
In these cases, Pkl infers the amend operation's parent value based on context:
* When assigning to a declared property, the property's default value is amended (<<amend-null, including `null`>>).
If there is no type associated with the property, an empty `Dynamic` object is amended.
* When assigning to an entry (e.g. a `Mapping` member) or element (e.g. a `Listing` member), the enclosing object's `default` property is applied to the corresponding index or key, respectively, to produce the value to be amended.
* In other cases, evaluation fails with the error message "Cannot tell which parent to amend".
The type annotation of a <<methods,method>> parameter is not used for inference.
In this case, the argument's type should be specified explicitly.
[source,{pkl}]
----
class Bird {
name: String
function listHatchlings(items: Listing<String>): Listing<String> = new {
for (item in items) {
"\(name):\(item)"
}
}
}
typedProperty: Bird = new { // <1>
name = "Swift"
}
untypedProperty = new { // <2>
hello = "world"
}
typedListing: Listing<Bird> = new {
new { // <3>
name = "Kite"
}
}
untypedListing: Listing = new {
new { // <4>
hello = "there"
}
}
typedMapping: Mapping<String, Bird> = new {
default { entryKey ->
name = entryKey
}
["Saltmarsh Sparrow"] = new { // <5>
name = "Sharp-tailed Sparrow"
}
}
amendedMapping = (typedMapping) {
["Saltmarsh Sparrow"] = new {} // <6>
}
class Aviary {
birds: Listing<Bird> = new {
new { name = "Osprey" }
}
}
aviary: Aviary = new {
birds = new { // <7>
new { name = "Kiwi" }
}
}
swiftHatchlings = typedProperty.listHatchlings(new { "Poppy"; "Chirpy" }) // <8>
----
<1> Assignment to a property with an explicitly declared type, amending `new Bird {}`.
<2> Assignment to an undeclared property in module context, amending `new Dynamic {}`.
<3> `Listing` element creation, amending implicit `default`, `new Bird {}`.
<4> `Listing` element creation, amending implicit `default`, `new Dynamic {}`.
<5> `Mapping` value assignment, amdending the result of applying `default` to `"Saltmarsh Sparrow"`, `new Bird { name = "Saltmarsh Sparrow" }`.
<6> `Mapping` value assignment _replacing_ the parent's entry, amending the result of applying `default` to `"Saltmarsh Sparrow"`, `new Bird { name = "Saltmarsh Sparrow" }`.
<7> Admending the property default value `new Listing { new Bird { name = "Osprey" } }`; the result contains both birds.
<8> Error: Cannot tell which parent to amend.
[[let-expressions]]
=== Let Expressions