mirror of
https://github.com/apple/pkl.git
synced 2026-01-11 22:30:54 +01:00
319 lines
8.3 KiB
Plaintext
319 lines
8.3 KiB
Plaintext
= Basic Configuration
|
||
include::ROOT:partial$component-attributes.adoc[]
|
||
|
||
In this first part of xref:index.adoc[the Pkl tutorial], you build familiarity with Pkl syntax and basic structure.
|
||
You also learn different ways to invoke Pkl to produce different formats.
|
||
|
||
== Basic values
|
||
|
||
Consider the following example Pkl file.
|
||
|
||
[source,{pkl}]
|
||
.intro.pkl
|
||
----
|
||
name = "Pkl: Configure your Systems in New Ways"
|
||
attendants = 100
|
||
isInteractive = true
|
||
amountLearned = 13.37
|
||
----
|
||
|
||
Running Pkl on this file gives
|
||
|
||
[source,shell]
|
||
----
|
||
$ pkl eval /Users/me/tutorial/intro.pkl
|
||
name = "Pkl: Configure your Systems in New Ways"
|
||
attendants = 100
|
||
isInteractive = true
|
||
amountLearned = 13.37
|
||
----
|
||
|
||
It may seem nothing happened.
|
||
However, Pkl tells you that it _accepts the input_.
|
||
In other words, you now know that `intro.pkl` does not contain any errors.
|
||
|
||
You can ask Pkl to print this configuration in a different format, using the `-f` option.
|
||
For example, JSON:
|
||
|
||
[source,shell]
|
||
----
|
||
$ pkl eval -f json /Users/me/tutorial/intro.pkl
|
||
{
|
||
"name": "Pkl: Configure your Systems in New Ways",
|
||
"attendants": 100,
|
||
"isInteractive": true,
|
||
"amountLearned": 13.37
|
||
}
|
||
----
|
||
|
||
Or _PropertyList_ format:
|
||
|
||
[source,shell]
|
||
----
|
||
$ pkl eval -f plist /Users/me/tutorial/intro.pkl
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||
<plist version="1.0">
|
||
<dict>
|
||
<key>name</key>
|
||
<string>Pkl: Configure your Systems in New Ways</string>
|
||
<key>attendants</key>
|
||
<integer>100</integer>
|
||
<key>isInteractive</key>
|
||
<true/>
|
||
<key>amountLearned</key>
|
||
<real>13.37</real>
|
||
</dict>
|
||
</plist>
|
||
----
|
||
|
||
Notice that Pkl generated `<string>`, `<integer>`, `<true/>` and `<real>` for the values in your configuration.
|
||
This means it has _both_ correctly derived the types of the literal values _and_ translated those types to the corresponding elements in the PropertyList.
|
||
xref:03_writing_a_template.adoc[Part III] goes into types in more detail.
|
||
|
||
|
||
== Structure: Classes, objects, modules
|
||
|
||
A configuration often requires more than just basic values.
|
||
Typically, you need some kind of (hierarchical) structure.
|
||
Pkl provides _immutable objects_ for this.
|
||
|
||
Objects have three kinds of members: properties, elements and entries.
|
||
First, look at the syntax for objects and their members.
|
||
|
||
=== Properties
|
||
|
||
[source,{pkl}]
|
||
.simpleObjectWithProperties.pkl
|
||
----
|
||
bird { // <1>
|
||
name = "Common wood pigeon" // <2>
|
||
diet = "Seeds"
|
||
taxonomy { // <3>
|
||
species = "Columba palumbus"
|
||
}
|
||
}
|
||
----
|
||
<1> This _defines_ `bird` to be an object
|
||
<2> For primitive values, Pkl has the `=` syntax (more on this later).
|
||
<3> Just like `bird {`, but to show that objects can be nested.
|
||
|
||
This defines an object called `bird` with three _named properties_: `name`, `diet`, and `taxonomy`.
|
||
The first two of these are strings, but `taxonomy` is another object.
|
||
This means properties in an object can have different types and objects can be nested.
|
||
|
||
=== Elements
|
||
|
||
Of course, you don't always have names for every individual structure in your configuration.
|
||
What if you want "just a bunch of things" without knowing how many?
|
||
Pkl offers _elements_ for this purpose.
|
||
Elements are object members, just like properties.
|
||
Where you index properties by their name, you index elements by an integer.
|
||
You can think of an object that only contains elements as _array_.
|
||
Much like arrays in many languages, you can use square brackets to access an element, for example, `myObject[42]`.
|
||
|
||
You write an element, by writing only an expression.
|
||
Pkl derives the index from the number of elements already in the object.
|
||
For example:
|
||
|
||
[source,{pkl}]
|
||
.simpleObjectsWithElements.pkl
|
||
----
|
||
exampleObjectWithJustIntElements {
|
||
100 // <1>
|
||
42
|
||
}
|
||
|
||
exampleObjectWithMixedElements {
|
||
"Bird Breeder Conference"
|
||
(2000 + 23) // <2>
|
||
exampleObjectWithJustIntElements // <3>
|
||
}
|
||
----
|
||
<1> When you write only the value (without a name), you describe an _element_.
|
||
<2> Elements don't have to be literal values; they can be arbitrary _expressions_.
|
||
<3> Elements can really be _any_ value, not just primitive values.
|
||
|
||
[[entries]]
|
||
=== Entries
|
||
|
||
Objects can have one more kind of member; _entries_.
|
||
Like a _property_, an _entry_ is "named" (technically _keyed_).
|
||
Unlike a property, the name does not need to be known at declaration time.
|
||
Of course, we need a syntax to tell entries apart from properties.
|
||
You write entry "names" by enclosing them in square brackets ("names" is quoted, because the names do not need to be strings; any value can index entries).
|
||
|
||
[source,{pkl}]
|
||
.simpleObjectsWithEntries.pkl
|
||
----
|
||
pigeonShelter {
|
||
["bird"] { // <1>
|
||
name = "Common wood pigeon"
|
||
diet = "Seeds"
|
||
taxonomy {
|
||
species = "Columba palumbus"
|
||
}
|
||
}
|
||
["address"] = "355 Bird St." // <2>
|
||
}
|
||
|
||
birdCount {
|
||
[pigeonShelter] = 42 // <3>
|
||
}
|
||
----
|
||
<1> The difference with properties is the notation of the key: `[<expression>]`.
|
||
<2> As with properties, entries can be primitive values or objects.
|
||
<3> Any object can be used as a key for an entry.
|
||
|
||
|
||
=== Mixed members
|
||
|
||
In the examples so far, you have seen objects with properties, object with elements and object with entries.
|
||
These object members can be freely mixed.
|
||
|
||
[source,{pkl}]
|
||
.mixedObject.pkl
|
||
----
|
||
mixedObject {
|
||
name = "Pigeon"
|
||
lifespan = 8
|
||
"wing"
|
||
"claw"
|
||
["wing"] = "Not related to the _element_ \"wing\""
|
||
42
|
||
extinct = false
|
||
[false] {
|
||
description = "Construed object example"
|
||
}
|
||
}
|
||
----
|
||
|
||
Notice, how properties (`name`, `lifespan` and `extinct`), elements (`"wing"`, `"claw"`, `42`) and entries (`"wing"`, `false`) are mixed together in this one object.
|
||
You don't have to order them by kind, and you don't require (other) special syntax.
|
||
|
||
=== Collections
|
||
|
||
This free-for-all mixing of object members can become confusing.
|
||
Also, target formats are often considerably more restrictive.
|
||
In the following example, you see what happens when you try to produce JSON from `mixedObject`:
|
||
|
||
[source,shell]
|
||
----
|
||
$ pkl eval -f json /Users/me/tutorial/mixedObject.pkl
|
||
–– Pkl Error ––
|
||
Cannot render object with both properties/entries and elements as JSON.
|
||
Object: "Pigeon"
|
||
|
||
89 | text = renderer.renderDocument(value)
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
at pkl.base#Module.output.text (https://github.com/apple/pkl/blob/0.24.0/stdlib/base.pkl#L90)
|
||
----
|
||
|
||
This is why Pkl has two special types of object, namely _listings_, which contain _exclusively_ elements, and _mappings_, which contain _exclusively_ entries.
|
||
Both listings and mappings _are_ "just objects," so, they don't require syntax besides that of objects:
|
||
|
||
[source,{pkl}]
|
||
.collections.pkl
|
||
----
|
||
birds { // <1>
|
||
"Pigeon"
|
||
"Parrot"
|
||
"Barn owl"
|
||
"Falcon"
|
||
}
|
||
|
||
habitats { // <2>
|
||
["Pigeon"] = "Streets"
|
||
["Parrot"] = "Parks"
|
||
["Barn owl"] = "Forests"
|
||
["Falcon"] = "Mountains"
|
||
}
|
||
----
|
||
<1> A listing containing four elements.
|
||
<2> A mapping containing four entries.
|
||
|
||
[NOTE]
|
||
====
|
||
_Technically_, the correct way to define `birds` and `habitats` is by using `new Listing {...}` and `new Mapping {...}` explicitly.
|
||
You will see what these mean in part xref:03_writing_a_template.adoc[three] of this tutorial.
|
||
====
|
||
|
||
When you render _this_ configuration as JSON, everything works:
|
||
|
||
[source,json]
|
||
----
|
||
{
|
||
"birds": [
|
||
"Pigeon",
|
||
"Parrot",
|
||
"Barn owl",
|
||
"Falcon"
|
||
],
|
||
"habitats": {
|
||
"Pigeon": "Streets",
|
||
"Parrot": "Parks",
|
||
"Barn owl": "Forests",
|
||
"Falcon": "Mountains"
|
||
}
|
||
}
|
||
----
|
||
|
||
Notice particularly, that you rendered the listing as a JSON _array_.
|
||
When you index the listing with an integer, you're referring to the element inside the listing at the corresponding position (starting from `0`).
|
||
For example:
|
||
|
||
[source,{pkl}]
|
||
.indexedListing.pkl
|
||
----
|
||
birds {
|
||
"Pigeon"
|
||
"Parrot"
|
||
"Barn owl"
|
||
"Falcon"
|
||
}
|
||
|
||
relatedToSnowOwl = birds[2]
|
||
----
|
||
results in
|
||
[source,{pkl}]
|
||
----
|
||
birds {
|
||
"Pigeon"
|
||
"Parrot"
|
||
"Barn owl"
|
||
"Falcon"
|
||
}
|
||
relatedToSnowOwl = "Barn owl"
|
||
----
|
||
|
||
== Exercises
|
||
|
||
1. Given the following JSON snippet (taken from W3C examples), write the `.pkl` file that produces this JSON:
|
||
|
||
+
|
||
[source,json]
|
||
----
|
||
{
|
||
"name": "Common wood pigeon",
|
||
"lifespan": 8,
|
||
"friends": {
|
||
"bird1": "Parrot",
|
||
"bird2": "Albatross",
|
||
"bird3": "Falcon"
|
||
}
|
||
}
|
||
----
|
||
|
||
2. For some reason, we decide we no longer need the birdX names of the different birds; we just need them as an array.
|
||
Change your solution to the previous question to produce the following JSON result:
|
||
|
||
+
|
||
[source,json]
|
||
----
|
||
{
|
||
"name": "Common wood pigeon",
|
||
"lifespan": 8,
|
||
"birds": ["Parrot", "Barn owl", "Falcon"]
|
||
}
|
||
----
|