
Lancé le 1er février 2024 dans sa version 0.25, l'équipe du géant de la technologie a fourni une "visite rapide" du langage dans un texte publié sur le site Web de documentation de Pkl. Selon cette présentation, Pkl est conçu autour d'une structure de valeurs clés, d'une manière proche de JSON, plutôt que d'instructions impératives comme beaucoup d'autres langages de programmation traditionnels. Apple a conçu Pkl pour se spécialiser dans la configuration, avec quelques caractéristiques de qualité de vie soignées pour faire tourner les têtes. En effet, Pkl prend en charge les listes de propriétés JSON, XML et YAML dès le lancement pour générer des fichiers de configuration statiques.
Voici la présentation de Pkl par Apple :
[QUOTE]Présentation de Pkl, un langage de programmation pour la configuration
Nous sommes ravis d'annoncer la première version open source de Pkl (prononcé Pickle), un langage de programmation pour la production de configuration.
Lorsque l'on pense à la configuration, il est courant de penser à des langages statiques tels que JSON, YAML, ou Property Lists. Bien que ces langages aient leurs propres mérites, ils ont tendance à ne pas être à la hauteur lorsque la configuration devient de plus en plus complexe. Par exemple, leur manque d'expressivité signifie que le code est souvent répété. En outre, il peut être facile de commettre des erreurs de configuration, car ces formats ne fournissent aucune validation propre.
Pour remédier à ces lacunes, les formats sont parfois améliorés par des outils auxiliaires qui ajoutent une logique spéciale. Par exemple, s'il est nécessaire de rendre le code plus " DRY ", une propriété spéciale est introduite pour comprendre comment résoudre les références et fusionner les objets entre eux. Ou encore, il est nécessaire de se prémunir contre les erreurs de validation ; une nouvelle méthode est donc créée pour valider une valeur de configuration par rapport à un type attendu. Très vite, ces formats deviennent presque des langages de programmation, mais des langages difficiles à comprendre et à écrire.
À l'autre extrémité du spectre, un langage à usage général peut être utilisé à la place. Des langages comme Kotlin, Ruby ou JavaScript deviennent la base des DSL qui génèrent des données de configuration. Bien que ces langages soient extrêmement puissants, ils peuvent être difficiles à utiliser pour décrire la configuration, car ils ne sont pas axés sur la définition et la validation des données. En outre, ces DSL ont tendance à être liés à leurs propres écosystèmes. Il est difficile d'utiliser un DSL Kotlin comme couche de configuration pour une application écrite en Go.
Nous avons créé Pkl parce que nous pensons que la configuration est mieux exprimée comme un mélange entre un langage statique et un langage de programmation polyvalent. Nous voulons prendre le meilleur des deux mondes ; fournir un langage qui est déclaratif et simple à lire et à écrire, mais amélioré avec des capacités empruntées aux langages à usage général. Lorsque vous écrivez Pkl, vous pouvez utiliser les caractéristiques du langage auxquelles vous vous attendez, comme les classes, les fonctions, les conditionnelles et les boucles. Vous pouvez construire des couches d'abstraction et partager du code en créant des paquets et en les publiant. Plus important encore, vous pouvez utiliser Pkl pour répondre à de nombreux types de besoins de configuration. Il peut être utilisé pour produire des fichiers de configuration statiques dans n'importe quel format, ou être intégré en tant que bibliothèque dans une autre application en cours d'exécution.
Nous avons conçu Pkl avec trois objectifs principaux :
- Assurer la sécurité en détectant les erreurs de validation avant le déploiement.
- S'adapter aux cas d'utilisation les plus simples comme les plus complexes.
- Être un plaisir à écrire, grâce à nos meilleures intégrations IDE.
Une visite rapide de Pkl
Nous avons créé Pkl pour que sa syntaxe soit familière aux développeurs et qu'il soit facile à apprendre. C'est pourquoi nous avons inclus des fonctionnalités telles que les classes, les fonctions, les boucles et les annotations de type.
Par exemple, voici un fichier Pkl (module) qui définit un schéma de configuration pour une application web imaginaire.
NOTE : Ce fichier définit des types et non des données. Il s'agit d'un modèle courant dans Pkl, que nous appelons "template".
Application.pkl
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | module Application /// The hostname that this server responds to. hostname: String /// The port to listen on. port: UInt16 /// The environment to deploy to. environment: Environment /// The database connection for this application database: Database class Database { /// The username for this database. username: String /// The password for this database. password: String /// The remote host for this database. host: String /// The remote port for this database. port: UInt16 /// The name of the database. dbName: String } typealias Environment = "dev"|"qa"|"prod" |
localhost.pkl
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | amends "Application.pkl" hostname = "localhost" port = 3599 environment = "dev" database { host = "localhost" port = 5786 username = "admin" password = read("env:DATABASE_PASSWORD") dbName = "myapp" } |
sidecars.pkl
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import "Application.pkl" hidden db: Application.Database = new { host = "localhost" username = "admin" password = read("env:DATABASE_PASSWORD") dbName = "myapp" } sidecars { for (offset in List(0, 1, 2, 3)) { (db) { port = 6000 + offset } } } |
- YAML
Code : Sélectionner tout 1
2$ export DATABASE_PASSWORD=hunter2 $ pkl eval --format yaml sidecars.pkl
Code : Sélectionner tout 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21sidecars: - username: admin password: hunter2 host: localhost port: 6000 dbName: myapp - username: admin password: hunter2 host: localhost port: 6001 dbName: myapp - username: admin password: hunter2 host: localhost port: 6002 dbName: myapp - username: admin password: hunter2 host: localhost port: 6003 dbName: myapp
- JSON
Code : Sélectionner tout 1
2$ export DATABASE_PASSWORD=hunter2 $ pkl eval --format json sidecars.pkl
Code : Sélectionner tout 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32{ "sidecars": [ { "username": "admin", "password": "hunter2", "host": "localhost", "port": 6000, "dbName": "myapp" }, { "username": "admin", "password": "hunter2", "host": "localhost", "port": 6001, "dbName": "myapp" }, { "username": "admin", "password": "hunter2", "host": "localhost", "port": 6002, "dbName": "myapp" }, { "username": "admin", "password": "hunter2", "host": "localhost", "port": 6003, "dbName": "myapp" } ] }
- XML
Code : Sélectionner tout 1
2$ export DATABASE_PASSWORD=hunter2 $ pkl eval --format xml sidecars.pkl
Code : Sélectionner tout 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33<?xml version="1.0" encoding="UTF-8"?> <root> <sidecars> <Database> <username>admin</username> <password>hunter2</password> <host>localhost</host> <port>6000</port> <dbName>myapp</dbName> </Database> <Database> <username>admin</username> <password>hunter2</password> <host>localhost</host> <port>6001</port> <dbName>myapp</dbName> </Database> <Database> <username>admin</username> <password>hunter2</password> <host>localhost</host> <port>6002</port> <dbName>myapp</dbName> </Database> <Database> <username>admin</username> <password>hunter2</password> <host>localhost</host> <port>6003</port> <dbName>myapp</dbName> </Database> </sidecars> </root>
Validation intégrée
La configuration est une question de données. Et les données doivent être valides.
Dans Pkl, la validation est réalisée à l'aide d'annotations de type. Et les annotations de type peuvent optionnellement avoir des contraintes définies sur elles.
Voici un exemple qui définit les contraintes suivantes :
- age doit être compris entre 0 et 130.
- name ne doit pas être vide.
- zipCode doit être une chaîne de cinq chiffres.
Person.pkl
Code : | Sélectionner tout |
1 2 3 4 5 6 7 | module Person name: String(!isEmpty) age: Int(isBetween(0, 130)) zipCode: String(matches(Regex("\\d{5}"))) |
alessandra.pkl
Code : | Sélectionner tout |
1 2 3 4 5 6 7 | amends "Person.pkl" name = "Alessandra" age = -5 zipCode = "90210" |
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ pkl eval alessandra.pkl –– Pkl Error –– Type constraint `isBetween(0, 130)` violated. Value: -5 5 | age: Int(isBetween(0, 130)) ^^^^^^^^^^^^^^^^^ at Person#age (file:///Person.pkl) 5 | age = -5 ^^ at alessandra#age (file:///alessandra.pkl) 106 | text = renderer.renderDocument(value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ at pkl.base#Module.output.text (https://github.com/apple/pkl/blob/0.25.0/stdlib/base.pkl#L106) |
Code : | Sélectionner tout |
name: String(length.isOdd, chars.first == chars.last)
Partage de paquets
Pkl permet de publier des paquets et de les importer comme dépendances dans un projet. Cela permet de partager facilement du code Pkl qui peut être utilisé dans d'autres projets.
Il est facile de créer ses propres paquets et de les publier comme des versions GitHub, ou de les télécharger où l'on veut.
Les paquets peuvent être importés via l'URI absolu :
Code : | Sélectionner tout |
1 2 3 4 5 | import "package://pkg.pkl-lang.org/pkl-pantry/pkl.toml@1.0.0#/toml.pkl" output { renderer = new toml.Renderer {} } |
PklProject
Code : | Sélectionner tout |
1 2 3 4 5 | amends "pkl:Project" dependencies { ["toml"] { uri = "package://pkg.pkl-lang.org/pkl-pantry/pkl.toml@1.0.0" } } |
Code : | Sélectionner tout |
1 2 3 4 5 | import "@toml/toml.pkl" output { renderer = new toml.Renderer {} } |
- pkl-pantry - une monorepo qui publie de nombreux paquets différents.
- pkl-k8s - modèles pour définir les descripteurs Kubernetes.
Liaisons linguistiques
Pkl peut produire de la configuration en tant que sortie textuelle, et il peut également être intégré en tant que bibliothèque dans d'autres langages grâce à nos liaisons linguistiques.
Lorsqu'il est lié à une langue, le schéma Pkl peut être généré sous forme de classes/structures dans la langue cible. Par exemple, l'exemple Application.pkl ci-dessus peut être généré en Swift, Go, Java et Kotlin. Pkl inclut même des commentaires de documentation dans le langage cible.
[LIST][*]Swift
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | import PklSwift public enum Application {} extension Application { public enum Environment: String, CaseIterable, Decodable, Hashable { case dev = "dev" case qa = "qa" case prod = "prod" } public struct Module: PklRegisteredType, Decodable, Hashable { public static var registeredIdentifier: String = "Application" /// The hostname that this server responds to. public var hostname: String /// The port to listen on. public var port: UInt16 /// The environment to deploy to. public var environment: Environment /// The database connection for this application public var database: Database public init(hostname: String, port: UInt16, environment: Environment, database: Database) { self.hostname = hostname self.port = port self.environment = environment self.database = database } } public struct Database: PklRegisteredType, Decodable, Hashable { public static var registeredIdentifier: String = "Application#Database" /// The username for this database. public var username: String /// The password for this database. public var password: String /// The remote host for this database. public var host: String /// The remote port for this database. public var port: UInt16 /// The name of the database. public var dbName: String public init(username: String, password: String, host: String, port: UInt16, dbName: String) { self.username = username self.password = password self.host = host self.port = port self.dbName = dbName } } } |
Application.pkl.go
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | package application type Application struct { // The hostname that this server responds to. Hostname string `pkl:"hostname"` // The port to listen on. Port uint16 `pkl:"port"` // The environment to deploy to. Environment Environment.Environment `pkl:"environment"` // The database connection for this application Database *Database `pkl:"database"` } |
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // Code generated from Pkl module `Application`. DO NOT EDIT. package application type Database struct { // The username for this database. Username string `pkl:"username"` // The password for this database. Password string `pkl:"password"` // The remote host for this database. Host string `pkl:"host"` // The remote port for this database. Port uint16 `pkl:"port"` // The name of the database. DbName string `pkl:"dbName"` } |
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // Code generated from Pkl module `Application`. DO NOT EDIT. package Environment import ( "encoding" "fmt" ) type Environment string const ( Dev Environment = "dev" Qa Environment = "qa" Prod Environment = "prod" ) // String returns the string representation of Environment func (rcv Environment) String() string { return string(rcv) } |
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | import java.lang.Object; import java.lang.Override; import java.lang.String; import java.lang.StringBuilder; import java.util.Objects; import org.pkl.config.java.mapper.Named; import org.pkl.config.java.mapper.NonNull; public final class Application { /** * The hostname that this server responds to. */ public final @NonNull String hostname; /** * The port to listen on. */ public final int port; /**... |
La fin de cet article est réservée aux abonnés. Soutenez le Club Developpez.com en prenant un abonnement pour que nous puissions continuer à vous proposer des publications.