IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Vous êtes nouveau sur Developpez.com ? Créez votre compte ou connectez-vous afin de pouvoir participer !

Vous devez avoir un compte Developpez.com et être connecté pour pouvoir participer aux discussions.

Vous n'avez pas encore de compte Developpez.com ? Créez-en un en quelques instants, c'est entièrement gratuit !

Si vous disposez déjà d'un compte et qu'il est bien activé, connectez-vous à l'aide du formulaire ci-dessous.

Identifiez-vous
Identifiant
Mot de passe
Mot de passe oublié ?
Créer un compte

L'inscription est gratuite et ne vous prendra que quelques instants !

Je m'inscris !

Apple lance Pkl, un langage de programmation open-source de type configuration en tant que code

Le , par Jade Emy

17PARTAGES

10  0 
Apple a lancé Pkl, un nouveau "langage de configuration intégrable" open source, dans l'espoir d'alléger les tâches de configuration, qu'elles soient petites ou grandes, simples ou complexes, ad hoc ou répétitives.

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 :

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"
Et voici comment les données de configuration peuvent être définies :

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"
}
Il est facile de créer des variations des mêmes données de base en les modifiant. Par exemple, imaginons que nous voulions exécuter quatre bases de données localement, en tant que sidecars. Ceci utilise un générateur de for pour produire quatre variations, chacune modifiant la base db et spécifiant un port différent.

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
    }
  }
}
Les programmes Pkl peuvent être facilement convertis en formats courants.

  • 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
    21
    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
  • JSON

    Code : Sélectionner tout
    1
    2
    $ export DATABASE_PASSWORD=hunter2
    $ pkl eval --format json sidecars.pkl
    Code : Sélectionner tout
    1
    2
    3
    {
      "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}")))
Une contrainte défaillante provoque une erreur d'évaluation.

alessandra.pkl
Code : Sélectionner tout
1
2
3
4
5
6
7
amends "Person.pkl"

name = "Alessandra"

age = -5

zipCode = "90210"
L'évaluation de ce module échoue :

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)
Les contraintes sont des expressions arbitraires. Cela vous permet de créer des types qui peuvent exprimer n'importe quel type de contrôle pouvant être exprimé dans Pkl. Voici un exemple de type qui doit être une chaîne de caractères de longueur impaire et dont la première lettre correspond à la dernière.

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 {}
}
Ils peuvent également être gérés comme des dépendances d'un projet. L'utilisation d'un projet permet à Pkl de résoudre les conflits de version entre les différentes versions d'une même dépendance au sein d'un graphe de dépendance. Cela signifie également que vous pouvez importer des paquets sous un nom plus simple.

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" }
}
myconfig.pkl
Code : Sélectionner tout
1
2
3
4
5
import "@toml/toml.pkl"

output {
  renderer = new toml.Renderer {}
}
Un ensemble de paquets sont maintenus par nous, l'équipe de Pkl. Il s'agit notamment de

  • 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.

  • 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
            }
        }
    }
  • Go

    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"`
    }
    Database.pkl.go
    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"`
    }
    environment/Environment.pkl.go
    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)
    }
  • Java

    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
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    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;
    
      /**
       * The environment to deploy to.
       */
      public final @NonNull Environment environment;
    
      /**
       * The database connection for this application
       */
      public final @NonNull Database database;
    
      public Application(@Named("hostname") @NonNull String hostname, @Named("port") int port,
          @Named("environment") @NonNull Environment environment,
          @Named("database") @NonNull Database database) {
        this.hostname = hostname;
        this.port = port;
        this.environment = environment;
        this.database = database;
      }
    
      public static final class Database {
        /**
         * The username for this database.
         */
        public final @NonNull String username;
    
        /**
         * The password for this database.
         */
        public final @NonNull String password;
    
        /**
         * The remote host for this database.
         */
        public final @NonNull String host;
    
        /**
         * The remote port for this database.
         */
        public final int port;
    
        /**
         * The name of the database.
         */
        public final @NonNull String dbName;
    
        public Database(@Named("username") @NonNull String username,
            @Named("password") @NonNull String password, @Named("host") @NonNull String host,
            @Named("port") long port, @Named("dbName") @NonNull String dbName) {
          this.username = username;
          this.password = password;
          this.host = host;
          this.port = port;
          this.dbName = dbName;
        }
      }
    
      public enum Environment {
        DEV("dev"),
    
        QA("qa"),
    
        PROD("prod");
    
        private String value;
    
        private Environment(String value) {
          this.value = value;
        }
    
        @Override
        public String toString() {
          return this.value;
        }
      }
    }
  • Kotlin

    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
    import kotlin.Int
    import kotlin.Long
    import kotlin.String
    
    data class Application(
      /**
       * The hostname that this server responds to.
       */
      val hostname: String,
      /**
       * The port to listen on.
       */
      val port: Int,
      /**
       * The environment to deploy to.
       */
      val environment: Environment,
      /**
       * The database connection for this application
       */
      val database: Database
    ) {
      data class Database(
        /**
         * The username for this database.
         */
        val username: String,
        /**
         * The password for this database.
         */
        val password: String,
        /**
         * The remote host for this database.
         */
        val host: String,
        /**
         * The remote port for this database.
         */
        val port: Int,
        /**
         * The name of the database.
         */
        val dbName: String
      )
    
      enum class Environment(
        val value: String
      ) {
        DEV("dev"),
    
        QA("qa"),
    
        PROD("prod");
    
        override fun toString() = value
      }
    }


La génération de code n'est qu'une des nombreuses façons d'intégrer Pkl dans une application. Nos liaisons linguistiques fournissent également des API d'évaluation pour contrôler l'évaluation de Pkl à un bas niveau, et les utilisateurs sont libres d'interagir avec Pkl au niveau d'abstraction qui a le plus de sens pour leur application.

Support de l'éditeur

Nous pensons qu'un langage de programmation n'est bon que dans la mesure où l'on peut l'écrire. C'est pourquoi nous visons à fournir le meilleur support d'éditeur possible. Lors de l'écriture de Pkl dans un éditeur, les utilisateurs sont guidés tout au long du processus de remplissage des données de configuration à partir d'un modèle donné. En outre, les éditeurs fournissent un retour d'information instantané si des valeurs sont invalides, et la documentation est immédiatement disponible en cas de besoin.

Nous lançons également notre plugin IntelliJ, qui fournit un support riche pour les éditeurs JetBrains, y compris IntelliJ, Webstorm, GoLand, et PyCharm. Ces plugins sont capables d'analyser un programme Pkl et de fournir des fonctionnalités telles que l'autocomplétion, la définition de go-to-definition et la prise en charge du refactoring.

Voici quelques-unes des fonctionnalités disponibles :

  • Autocompletion

  • Navigation

  • validation



En outre, nous prévoyons également de prendre en charge le protocole de serveur de langue, qui fournira un niveau d'intégration similaire dans d'autres éditeurs.

NOTE : Nous lançons également deux autres plugins : notre plugin VS Code et notre plugin neovim. Aujourd'hui, ces plugins n'offrent que des fonctionnalités d'édition de base telles que la coloration syntaxique et le pliage de code.

Prochaines étapes

Nous espérons que vous avez apprécié ce que nous vous avons montré jusqu'à présent. Pour un guide plus approfondi, jetez un coup d'œil à notre tutoriel. Pour en savoir plus sur le langage lui-même, lisez notre référence sur le langage. Pour nous contacter, n'hésitez pas à soumettre un sujet sur GitHub Discussions.

De plus, n'hésitez pas à parcourir nos exemples de dépôts pour vous faire une idée de l'utilisation de Pkl.

Pour essayer Pkl localement, téléchargez notre CLI en suivant notre guide d'installation. De plus, essayez d'installer l'un de nos différents plugins d'édition pour avoir un aperçu de ce que c'est que d'écrire Pkl soi-même.

Nous sommes ravis de partager Pkl avec vous, et nous ne faisons que commencer. Nous sommes impatients de voir ce que vous pourrez en faire !
Source : Apple

Et vous ?

Quel est votre avis sur le sujet ?

Voir aussi :

Les langages de programmation les mieux payés en 2023 : une analyse de 10 millions d'offres d'emploi effectuée par DevJobsScanner

La réécriture en Swift du framework Foundation d'Apple sera en open source, l'entreprise affirme que la réécriture du framework permettra d'améliorer les performances

Python et SQL en tête des langages des programmations les plus populaires de 2023 sur IEEE Spectrum. Java, C++, C et JavaScript complètent les tops 5

Une erreur dans cette actualité ? Signalez-nous-la !

Avatar de unanonyme
Membre éclairé https://www.developpez.com
Le 06/02/2024 à 9:37
C'est une bonne initiative, en principe, en action ça à l'air sympa,
surtout l'intégration native multi langages, c'est vraiment bien pensé.
J'ai dans l'idée que ça fera son trou.

En pratique, on verra dans 5 ans le genre de propriétés émergente
que fera apparaître le déploiement à l'échelle d'une telle innovation.
Spoiler, on a pas fini d'innover.
1  0 
Avatar de dglaude
Futur Membre du Club https://www.developpez.com
Le 07/02/2024 à 21:06
Est-ce qu'un nouveau langage peut naître et être utilisé alors que ChatGPT ou CoPilote ne peut aider les programmeurs?
Est-ce que l'IA a déjà révolutionner le codage au point d'être un frein à l'innovation?
0  0