mirror of
https://github.com/apple/pkl.git
synced 2026-03-23 01:29:20 +01:00
Initial commit
This commit is contained in:
317
docs/modules/language-tutorial/pages/01_basic_config.adoc
Normal file
317
docs/modules/language-tutorial/pages/01_basic_config.adoc
Normal file
@@ -0,0 +1,317 @@
|
||||
= 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
|
||||
|
||||
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"]
|
||||
}
|
||||
----
|
||||
Reference in New Issue
Block a user