Chaque langage de programmation a des opinions. Python croit que la lisibilité compte. Rust croit que la sûreté n'est pas négociable. Go croit que la simplicité est une fonctionnalité, pas une limitation.
FLIN a cinq opinions, et ce ne sont pas des recommandations. Ce sont des lois. Chaque décision syntaxique, chaque mot-clé, chaque choix architectural est testé contre ces cinq principes. Si une fonctionnalité viole l'un d'entre eux, elle n'est pas livrée.
Ces principes n'ont pas été découverts par itération. Ils ont été énoncés au premier jour, avant que la première ligne de Rust ne soit écrite pour le compilateur, avant que le premier token ne soit défini dans le lexer. Ils sont la constitution du langage, et cet article explique chacun en détail -- ce qu'il signifie, pourquoi il compte, et comment il se manifeste dans le code.
Principe 1 : SIMPLE
Si un enfant de douze ans qui connaît le HTML ne peut pas comprendre votre code FLIN en trente secondes, le code est trop complexe.
C'est la Règle d'Or. Elle est imprimée sur la première page de la spécification du langage. Elle gouverne chaque décision syntaxique que nous prenons.
La simplicité dans FLIN ne consiste pas à être "facile" ou "adapté aux débutants" -- ce sont des effets secondaires. Il s'agit de charge cognitive. Chaque élément de syntaxe qu'un développeur doit apprendre, retenir et reconnaître en lisant du code est une taxe sur sa capacité mentale. FLIN minimise cette taxe impitoyablement.
Considérez comment d'autres langages gèrent un compteur réactif :
jsx// React : 10 lignes, nécessite de comprendre les imports, les hooks, JSX, les fonctions fléchées
import React, { useState } from 'react';
export default function Counter() {
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
);
}svelte<!-- Svelte 5 : 5 lignes, nécessite de comprendre les runes, les blocs script -->
<script>
let count = $state(0);
</script>
<button onclick={() => count++}>{count}</button>flin// FLIN : 3 lignes, nécessite de comprendre les variables et le HTML
count = 0
<button click={count++}>{count}</button>La version FLIN nécessite exactement trois connaissances : comment fonctionnent les variables, comment fonctionnent les éléments HTML, et comment les accolades insèrent des expressions. Un enfant de douze ans qui a écrit une page HTML peut lire ce code et le comprendre en trente secondes.
Le principe de simplicité a des conséquences concrètes sur la conception du langage :
Pas d'imports. Dans FLIN, tout est disponible. Il n'y a pas de import React from 'react', pas de from fastapi import FastAPI, pas de use std::collections::HashMap. Le fichier est le composant. Les composants d'autres fichiers sont référencés par leur nom de fichier. Le compilateur résout tout.
Pas d'export. Le fichier est le composant. Ce que le fichier définit est ce que le composant expose. Il n'y a pas de export default, pas de module.exports, pas de modificateur pub.
Pas de fonctions enveloppantes. Dans React, chaque composant est une fonction. Dans Vue, chaque composant est un objet avec une méthode setup(). Dans FLIN, le fichier lui-même est le composant. Il n'y a pas de syntaxe d'enveloppement.
flin// Ce fichier entier EST un composant appelé "Greeting"
// Nom de fichier : Greeting.flin
name = props.name || "World"
<h1>Bonjour, {name} !</h1>Comparez avec l'équivalent React :
jsx// React : enveloppement de fonction, instruction return, JSX
export default function Greeting({ name = "World" }) {
return <h1>Bonjour, {name} !</h1>;
}La version FLIN a moins de syntaxe, mais elle n'est pas moins puissante. Elle obtient le même résultat en éliminant la cérémonie qui existe pour satisfaire l'architecture du framework, pas pour exprimer l'intention du développeur.
Le test de simplicité est appliqué au moment de la revue de code. Quand nous concevons une nouvelle fonctionnalité pour FLIN, nous écrivons trois programmes exemples qui l'utilisent. Puis nous montrons ces programmes à quelqu'un qui ne connaît pas FLIN et demandons : "Pouvez-vous me dire ce que cela fait ?" S'il ne peut pas répondre en trente secondes, la syntaxe est mauvaise. Pas la personne -- la syntaxe.
Principe 2 : ZÉRO-CONFIG
Un fichier .flin est tout ce dont vous avez besoin.Ce principe élimine l'explosion de fichiers de configuration qui afflige le développement web moderne. Dans un projet Next.js typique, vous avez : package.json, tsconfig.json, next.config.js, tailwind.config.js, postcss.config.js, .eslintrc.js, .prettierrc, et plus encore. Chaque fichier existe parce qu'un outil a besoin qu'on lui dise comment se comporter.
FLIN n'a besoin d'aucune configuration parce que FLIN est le seul outil. Il n'y a pas de bundler à configurer, pas de compilateur TypeScript à mettre en place, pas de linter à personnaliser, pas de gestionnaire de paquets à initialiser.
Le projet FLIN minimum viable est un fichier :
my-app/
app.flinC'est tout. Lancez flin dev, et vous avez un serveur de développement avec rechargement à chaud, vérification de types et accès à la base de données. Lancez flin build, et vous avez un binaire de production.
Un projet plus grand utilise plus de fichiers, mais zéro fichier de configuration :
my-app/
index.flin # Page d'accueil
about.flin # Page À propos
products/
index.flin # Liste de produits
[id].flin # Détail produit
api/
users.flin # API utilisateurs
users/[id].flin # API détail utilisateur
components/
Header.flin
Footer.flin
ProductCard.flin
styles.css # Styles globaux optionnelsChaque fichier est soit un fichier source .flin soit une feuille de style .css. Il n'y a pas de flin.config.js. Il n'y a pas de flin.toml. Le comportement du langage est défini par la spécification du langage, pas par une configuration par projet.
Le principe zéro-config a un corollaire critique : la convention plutôt que la configuration, poussée à sa conclusion logique. Le routage basé sur les fichiers n'est pas configuré ; il est dérivé de la structure des répertoires. Les définitions d'entités créent automatiquement les tables de base de données. Les variables d'environnement suivent des conventions de nommage au lieu de nécessiter des fichiers .env.
Le runtime FLIN gère :
Fonctionnalité Fonctionnement
--------------------------- ----------------------------------
Rechargement à chaud Automatique (surveille les fichiers .flin)
Vérification de types Intégrée au compilateur
Bundling Automatique (sortie unique)
Base de données Embarquée (FlinDB, zéro setup)
Variables d'environnement Nommage par convention
Routage Basé sur les fichiers (structure de répertoires)
Formatage Intégré (un style, appliqué)Sept fonctionnalités qui, dans l'écosystème JavaScript, nécessitent sept outils séparés avec sept fichiers de configuration séparés. Dans FLIN, elles nécessitent zéro.
Principe 3 : RÉACTIF
Toutes les variables sont réactives par défaut.
Dans React, vous devez opter pour la réactivité en appelant useState. Dans Vue 3, vous devez envelopper les valeurs dans ref() ou reactive(). Dans Svelte 5, vous devez utiliser $state(). Dans Angular, vous devez utiliser des signaux ou des observables. Chaque framework a sa propre primitive de réactivité, sa propre API, son propre modèle mental.
La position de FLIN est que la réactivité est trop fondamentale pour être opt-in. Chaque variable dans FLIN est réactive. Quand sa valeur change, chaque expression de vue qui en dépend se met à jour automatiquement.
flinfirstName = "Juste"
lastName = "GNIMAVO"
theme = "light"
// Les trois spans se mettent à jour indépendamment quand leur dépendance change
<div class={theme}>
<span>{firstName}</span>
<span>{lastName}</span>
<span>{firstName + " " + lastName}</span>
</div>Le compilateur effectue l'analyse des dépendances au moment de la compilation. Il sait que le premier <span> dépend de firstName, le deuxième de lastName, et le troisième des deux. Quand firstName change, seuls le premier et le troisième span se mettent à jour. Quand lastName change, seuls le deuxième et le troisième. C'est de la réactivité à grain fin -- la même approche utilisée par Solid.js et Svelte 5 -- mais sans aucune API de réactivité explicite.
Le principe réactif s'étend aux valeurs calculées :
flinitems = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
filter = "even"
// Cette valeur se recalcule automatiquement quand items ou filter change
filtered = match filter {
"even" -> items.where(x => x % 2 == 0)
"odd" -> items.where(x => x % 2 != 0)
_ -> items
}
<div>
<select change={filter = event.target.value}>
<option value="all">Tous</option>
<option value="even">Pairs</option>
<option value="odd">Impairs</option>
</select>
{for item in filtered}
<span>{item}</span>
{/for}
</div>Quand l'utilisateur sélectionne "Impairs" dans le menu déroulant, filter change. Parce que filtered dépend de filter, il se recalcule. Parce que la boucle {for} dépend de filtered, elle se re-rend. La chaîne entière -- de l'interaction utilisateur à la mise à jour du DOM -- est automatique.
Dans React, réaliser cela nécessiterait useState pour items, useState pour filter, et useMemo pour filtered. Trois hooks, trois appels d'API, trois occasions d'oublier une dépendance ou d'introduire un bug de closure obsolète. Dans FLIN, le compilateur gère tout.
Pourquoi réactif par défaut plutôt que réactif en opt-in ? Parce que la réactivité opt-in introduit une catégorie de bugs qui ne devrait pas exister. Dans React, le bug le plus courant est d'oublier d'utiliser useState -- écrire let count = 0 au lieu de const [count, setCount] = useState(0) et se demander pourquoi l'interface ne se met pas à jour. Dans FLIN, ce bug ne peut pas exister parce que chaque variable est réactive. Il n'y a pas de variable non-réactive à utiliser accidentellement.
Principe 4 : INTENT-NATIVE
Exprimez ce que vous voulez, pas comment le faire.
C'est le plus prospectif des cinq principes de FLIN. Il reconnaît que dans l'ère de l'IA, la frontière entre ce qu'un programmeur spécifie et ce qu'un compilateur infère peut être repoussée bien plus loin que ce que les langages traditionnels permettent.
La conception intent-native se manifeste dans deux mots-clés : ask et search.
Le mot-clé ask traduit le langage naturel en requêtes de base de données :
flin// Approche traditionnelle : écrire la requête soi-même
recent_buyers = User
.where(active == true)
.where(created > last_week)
.where(id in Purchase.where(amount > 5000).map(p => p.user_id))
.order(created, "desc")
.limit(20)
// Approche intent-native : décrire ce que vous voulez
recent_buyers = ask "utilisateurs actifs inscrits la semaine dernière ayant fait un achat de plus de 5000"Les deux produisent le même résultat. La version ask délègue la construction de la requête à un modèle d'IA, qui traduit la description en langage naturel en opérations d'entité appropriées. Ce n'est pas de l'interpolation de chaînes ou du pattern matching -- c'est une compréhension sémantique du modèle de données et de l'intention de la requête.
Le mot-clé search effectue une recherche sémantique sur les champs marqués avec le modificateur semantic :
flinentity Article {
title: text
content: semantic text
tags: [text]
published: time = now
}
// Recherche par mots-clés traditionnelle : correspondances exactes uniquement
results = Article.where(title.contains("machine learning"))
// Recherche sémantique : comprend le sens, pas juste les mots-clés
results = search "articles sur les tendances IA dans l'agriculture africaine"
in Article
by content
limit 10La recherche sémantique ne cherche pas les mots exacts "IA", "tendances", "africaine" ou "agriculture". Elle comprend le sens de la requête et retourne des articles qui sont conceptuellement liés, même s'ils utilisent un vocabulaire différent. Un article intitulé "Comment le deep learning transforme les rendements agricoles en Afrique de l'Ouest" correspondrait, même s'il ne partage aucun mot-clé avec la requête.
Pourquoi intent-native est-il un principe de conception et pas juste une fonctionnalité ? Parce qu'il change la façon dont les développeurs pensent à la programmation. La programmation traditionnelle est impérative : vous dites à l'ordinateur chaque étape. FLIN permet l'intention déclarative : vous dites à l'ordinateur le résultat que vous voulez, et il détermine les étapes.
Il ne s'agit pas de remplacer la programmation par du langage naturel. Les mots-clés ask et search coexistent avec la syntaxe de requête traditionnelle de FLIN. Un développeur peut écrire User.where(active == true) quand il connaît la requête exacte, et ask "utilisateurs actifs ayant acheté ce mois-ci" quand la requête est complexe ou exploratoire.
Le principe intent-native est conçu pour un avenir où des agents IA écrivent la plupart du code. Un langage qui supporte les requêtes en langage naturel est plus facile à générer correctement pour une IA qu'un langage qui nécessite une syntaxe SQL précise. FLIN n'est pas seulement un langage pour les développeurs humains -- c'est un langage pour les assistants IA qui écriront de plus en plus de code à leurs côtés.
Principe 5 : MEMORY-NATIVE
Tout est persisté et possède un historique.
C'est le principe qui donne son nom à FLIN. "E flin nu" -- il se souvient des choses.
Dans tout langage de programmation traditionnel, la persistance est une réflexion après coup. Vous construisez votre logique applicative, puis vous déterminez comment sauvegarder les données. Vous choisissez une base de données, installez un pilote, configurez une connexion, écrivez un schéma, lancez les migrations, et puis -- enfin -- votre application peut se souvenir des choses entre les redémarrages.
Dans FLIN, la persistance est le comportement par défaut. Le mot-clé entity crée à la fois un type et une table de base de données. Le mot-clé save écrit dans la base de données. L'opérateur @ interroge l'historique. Tout fonctionne dès la sortie de la boîte, sans configuration.
flinentity Metric {
name: text
value: number
recorded: time = now
}
// Sauvegarder la métrique du jour
save Metric { name: "revenue", value: 42500 }
// Demain, comparer avec hier
revenue = Metric.where(name == "revenue").first
yesterdayRevenue = revenue @ yesterday
change = ((revenue.value - yesterdayRevenue.value) / yesterdayRevenue.value) * 100
<div class="dashboard">
<h2>Chiffre d'affaires</h2>
<span class="current">{revenue.value}</span>
<span class={if change > 0 then "up" else "down"}>
{change.to_fixed(1)}%
</span>
<h3>Historique</h3>
{for version in revenue.history.last(7)}
<div>
<span>{version.recorded.format("MMM D")}</span>
<span>{version.value}</span>
</div>
{/for}
</div>Ce code affiche le chiffre d'affaires du jour, le compare avec celui d'hier, calcule le pourcentage de variation et montre les sept derniers jours d'historique. Dans une pile traditionnelle, cela nécessiterait : une base de données avec une table d'audit, un cron job ou un déclencheur pour enregistrer les valeurs historiques, un endpoint backend pour calculer les deltas, et un composant frontend pour afficher le tout.
Dans FLIN, c'est un seul fichier.
Le principe memory-native a trois composantes techniques :
Versionnage automatique. Chaque opération save crée une nouvelle version de l'entité. La version précédente n'est pas écrasée ; elle est conservée dans le stockage temporel de FlinDB. C'est similaire à la façon dont Git stocke chaque commit plutôt que d'écraser les fichiers -- mais appliqué aux données applicatives.
Requêtes temporelles. L'opérateur @ fournit l'accès aux versions passées en utilisant plusieurs styles de référence :
flinuser @ -1 // Version précédente
user @ -3 // Trois versions en arrière
user @ yesterday // Version d'hier
user @ last_week // Version de la semaine dernière
user @ "2026-01-15" // Version à une date spécifique
user.history // Toutes les versions jamais enregistréesRecherche sémantique. Les champs marqués avec semantic text sont automatiquement transformés en vecteurs, permettant la recherche par similarité sans moteur de recherche séparé :
flinentity Product {
name: text
description: semantic text
price: money
}
// Trouver des produits similaires en sens à la requête
results = search "ordinateur portable léger pour les voyages" in Product by description limit 5Ces trois composantes -- versionnage, requêtes temporelles et recherche sémantique -- forment la "mémoire" de FLIN. Ce ne sont pas des plugins. Ce ne sont pas des fonctionnalités optionnelles. C'est le langage.
Comment les cinq principes interagissent
Les cinq principes ne sont pas indépendants. Ils se renforcent mutuellement de façons qui créent des propriétés de conception émergentes.
Simple + Zéro-Config signifie que l'expérience complète de "démarrage" est : créer un fichier, écrire du code, lancer flin dev. Il n'y a pas de phase de configuration, pas de phase d'installation, pas de "installez ces douze dépendances". Le workflow le plus simple possible.
Réactif + Memory-Native signifie que quand des données persistantes changent, l'interface se met à jour automatiquement. Sauvegardez une nouvelle entité, et toute vue qui interroge ce type d'entité se re-rend. Cela élimine toute la catégorie de bugs de "données obsolètes" qui affligent les SPA traditionnelles.
Intent-Native + Memory-Native signifie que les requêtes alimentées par l'IA opèrent sur des données historiquement riches. Vous pouvez ask "utilisateurs dont les dépenses ont diminué par rapport au mois dernier" -- une requête qui nécessite une comparaison temporelle, qui n'est possible que parce que FLIN se souvient de chaque version de chaque entité.
Simple + Réactif signifie que le développeur n'a jamais à choisir entre "la façon simple" et "la façon réactive". Il n'y a qu'une seule façon de déclarer une variable (count = 0), et elle est toujours réactive. Le chemin simple est le chemin correct.
Zéro-Config + Intent-Native signifie que les fonctionnalités IA fonctionnent dès la sortie de la boîte. Vous n'avez pas besoin de configurer une clé API OpenAI, de mettre en place un pipeline d'embeddings ou d'installer une base de données vectorielle. Le runtime FLIN gère tout.
Ensemble, les cinq principes créent un langage où le modèle mental du développeur est radicalement plus simple que dans tout framework existant :
Modèle mental traditionnel Modèle mental FLIN
----------------------------------------- -------------------------
Apprendre React + hooks + JSX Apprendre la syntaxe FLIN
Configurer TypeScript (types intégrés)
Choisir une gestion d'état (toutes les variables réactives)
Configurer base de données + ORM + migrations entity + save
Installer un moteur de recherche mot-clé search
Configurer bundler + linter + formateur (intégré, zéro config)
Écrire des routes API avec Express mot-clé route
Déployer avec Docker flin build (binaire unique)Huit décisions dans la pile traditionnelle. Zéro décision dans FLIN. Non pas parce que FLIN a des opinions sur quelle base de données utiliser -- il n'utilise pas de base de données externe. Non pas parce que FLIN a des opinions sur quel bundler utiliser -- il n'utilise pas de bundler. Les décisions n'existent pas parce que les problèmes n'existent pas.
Les principes comme filtre
La fonction la plus importante des cinq principes est celle de filtre pour les nouvelles fonctionnalités. Quand quelqu'un propose d'ajouter quelque chose à FLIN, nous posons cinq questions :
- Simple ? Un enfant de douze ans peut-il comprendre la syntaxe en trente secondes ?
- Zéro-Config ? Est-ce que cela fonctionne sans aucune configuration ?
- Réactif ? Est-ce que cela s'intègre au système de réactivité automatiquement ?
- Intent-Native ? Peut-il être exprimé comme une intention plutôt que comme des instructions ?
- Memory-Native ? Est-ce que cela respecte la persistance et l'historique ?
Si la réponse à l'une de ces questions est "non", la fonctionnalité a besoin d'être reconçue. Si la réponse à l'une est "ce serait possible, mais ce serait compliqué", la fonctionnalité a besoin d'être simplifiée. Si la réponse aux cinq est "oui", la fonctionnalité a sa place dans FLIN.
Ce filtre a rejeté de nombreuses fonctionnalités que d'autres langages considèrent essentielles. Les génériques ? Ils échouent au test de simplicité. Les fichiers de configuration ? Ils violent le zéro-config. La gestion d'état manuelle ? Elle contredit le réactif-par-défaut. Les requêtes SQL brutes ? Elles contournent l'intent-native. La persistance opt-in ? Elle sape le memory-native.
Ces rejets ne sont pas des compromis. Ce sont les principes qui font leur travail.
Prochain dans la série : La Règle d'Or : un fichier .flin suffit -- Pas de package.json. Pas de tsconfig. Pas de webpack.config. Pas de postcss.config. Un fichier.