MirageOS - un micro OS (unikernel) en OCaml

67
21
déc.
2021
Distribution

MirageOS est un outil permettant de créer un unikernel (un système d’exploitation) pouvant faire office de micro-service comme un site Internet, un service SMTP ou encore un service DNS. L’objectif de MirageOS est de proposer une solution modulaire afin que l’utilisateur puisse créer son propre système selon ce qu’il souhaite vraiment. La modularité et l’approche clean-state de MirageOS permet de délester le système final d’éléments superficiels à priori. De ce fait, MirageOS est capable de produire un système d’exploitation complet comme un simple site internet ne pesant au final que ~16 Mo.

L’approche de MirageOS est de reconstruire tous les éléments de votre application finale en OCaml (en partant de votre API REST à la pile TCP/IP). Les logiques d’abstraction et la modularité d’OCaml sont les bases de MirageOS afin de s’abstraire de tout ce qui est à proprement parler lié au système (les syscalls) et de pouvoir interchanger une implémentation avec une autre sans changer le reste de l’application. Par ces mécanismes-là, MirageOS a la possibilité de produire un simple exécutable ou de produire un système complet capable d’être virtualisé avec Xen ou KVM.

Dans cette dépêche, nous allons voir ce qu’est concrètement MirageOS et expliquer comment l’utiliser.

Sommaire

Un peu d’OCaml

Avant de parler de MirageOS, nous avons besoin d’une petite introduction à OCaml. OCaml est un langage né en 1996 et maintenu par l’Inria venant de la famille des langages ML (contraction de Meta Language : un langage de programmation généraliste fonctionnel). Il propose un système de types ainsi qu’une gestion automatique de la mémoire. Nous allons ici nous intéresser aux points qui ont dirigé MirageOS vers ce langage.

Un système de modules

OCaml a un système de modules, qui est régi par une règle : un fichier OCaml, un module ou unité de compilation. Un module est une implémentation d’un certain type de données ainsi que des fonctions permettant de traiter/manipuler ce type de données. On peut par exemple parler du module String qui implémente les chaînes de caractères ainsi que les fonctions comme find_character/trim/ etc.

La particularité d’OCaml réside dans l’idée qu’une implémentation peut être décrite par une signature/interface, le fichier *.mli. Ce dernier décrit ce qui doit être montré aux autres modules de son implémentation associée. Il est courant en OCaml d’abstraire/cacher le type de données et de décrire uniquement les fonctions associées au type. Il est même commun d’utiliser une signature/interface à plusieurs implémentations tel que :

module type ORDERED = sig
  type t

  val compare : t -> t -> int
end

Ici, on décrit une interface qui expose compare. Cette dernière peut être utilisée avec type t = string (et utiliser memcmp) ou un entier type t = int (et utiliser la soustraction).

Foncteur

Grâce à ce mécanisme de modules, on a la possibilité de produire un module/une implémentation à partir d’un autre module décrit par une interface tel que Ordered. Ainsi, un simple dictionnaire associant une clé avec une valeur peut se spécialiser selon la clé tel que :

module Make (S : ORDERED) : sig
  type 'a t

  val empty : 'a t
  val add : S.t -> 'a -> 'a t -> 'a t
end

Ce principe d’abstraction en OCaml est largement utilisé par la communauté, mais on le retrouve à une autre échelle en ce qui concerne MirageOS.

Fonctoriser les syscalls

Le principe de MirageOS et de son écosystème est de ne pas dépendre d’un appel système tel que ceux offerts par le noyau Linux. L’objectif est de s’abstraire des syscalls et d’injecter leurs implémentations à la production du système d’exploitation. De ce fait, on est en capacité autant d’introduire les syscalls usuels proposés par le noyau Linux comme ceux d’un micro-noyau pouvant être virtualisé avec Xen ou KVM.

L’idée est d’être en capacité de produire la logique applicative (votre service comme un site-web) qui est complétement abstrait de la logique du système - la stack TCP/IP, la couche de cryptographie, le système de fichiers. De cette manière, il devient possible de compiler votre application comme un exécutable ou comme un système entier qui peut être virtualisé via Xen ou KVM ou encore un système pouvant fonctionner sur des puces ESP32.

L’objectif est que le côté applicatif ne devrait pas changer.

Gestion de la mémoire

Depuis la version 3.9 (octobre 2020), seul le mode PVH(VM) est pris en charge, la partie bas niveau permettant le boot est assurée par Solo5 et la gestion de la mémoire est passée à la version de Doug Lea pour malloc.

L’éco-système de MirageOS

Ainsi, tout l’éco-système de MirageOS se fonde sur un principe d’abstraction de ce qui peut être considéré comme le système d’exploitation. Au-delà de ça, les composants du système sont eux-mêmes abstraits à l’aide d’interfaces comme ORDERED. Ainsi, MirageOS se définit plus comme un outil permettant de faire la glue entre plusieurs composants à l’aide d’interfaces.

Par ce biais, il devient simple par exemple d’interchanger l’implémentation d’un type de données de l’un à l’autre sans changer le reste de la logique du système. C’est, concrètement, ce qui se passe lorsqu’on injecte la stack TCP/IP du système hôte lorsqu’on veut produire un simple exécutable avec MirageOS ou qu’on utilise une implémentation de la stack TCP/IP en OCaml: mirage-tcpip.

Bien entendu, cette approche requiert que ces implémentations existent ! Et c’est le principal travail de l’équipe de MirageOS : implémenter des formats/protocoles/logiques qui peuvent être utilisés avec MirageOS. Au travers de ce travail d’abstraction, ces différentes implémentations peuvent être utilisées en dehors de MirageOS. Ainsi, la plupart des projets de MirageOS sont utilisés par la communauté dans d’autres contextes que celui de MirageOS comme :

  • Windows
  • Mac OSX
  • votre navigateur web grâce à js_of_ocaml

Quelques super-stars

Dans ces projets qui sont utilisés par d’autres personnes en dehors de MirageOS, nous avons :

  • irmin
  • mirage-tcpip
  • cohttp
  • ocaml-tls
  • ocaml-dns

Irmin

L’idée d’un système de fichiers n’est pas garantie par MirageOS et, même si nous avons essayé d’implémenter certains formats, l’équipe MirageOS a décidé de concentrer ses efforts dans l’implémentation d’un Key-Value store. Irmin est une abstraction de ce que devrait être une telle base de données. Mais, à la différence des systèmes tels que LMDB ou encore Git, Irmin n’offre qu’une abstraction commune. Ensuite, c’est à l’utilisateur de choisir son implémentation.

MirageOS utilise aujourd’hui Irmin avec une implémentation de Git en OCaml. Par ce dernier, un unikernel peut obtenir une base de données clé-valeur interne qui peut se synchroniser au boot avec un dépôt Git. Sur ce dernier, il peut se synchroniser ou il peut le mettre à jour. On parle alors de système de base de données persistant – même si le système s’éteint, il peut reprendre l’état dans lequel la base de données était juste avant de s’éteindre.

Irmin est actuellement utilisé par la crypto-monnaie Tezos afin de manipuler la block-chain.

Une autre utilisation concrète d’Irmin avec MirageOS est un système d’exploitation qui fait office de serveur DNS primaire dont le fichier zone est stocké dans un dépôt Git. Ainsi, l’unikernel se synchronise avec ce dépôt (avec le protocol Git - comme un git clone), il peut le modifier (comme git push) et l’utilisateur peut tout autant modifier aussi et demander ensuite à l’unikernel de se resynchroniser (comme git pull).

Dans le dernier cas, l’utilisateur peut décrire sa politique de merge (comment résoudre un conflit s’il y en a un) avec Irmin. Cela permet d’assurer la persistance des données en dehors de l’unikernel lui-même.

mirage-tcpip

Du fait que MirageOS est un système d’exploitation complet, l’équipe de MirageOS a finalement implémenté la stack TCP/IP au travers du projet mirage-tcpip. Au-delà de l’intérêt technique de ré-implementer une stack TCP/IP, cette dernière est industriellement utilisée par Docker.

Ce projet permet d’introduire un concept fondateur de MirageOS. L’objectif de l’outil mirage est de produire, au mieux, un système complet capable d’être virtualisé sur KVM ou Xen, mais il permet aussi de produire un simple exécutable UNIX comme nous aurions l’habitude d’avoir. Le point crucial ici est la capacité de mirage à orchestrer (indépendamment de l’application) les stacks selon la cible.

Pour ce qui est de la production d’un simple exécutable, mirage va injecter la stack TCP/IP du système hôte. Pour ce qui est de KVM ou Xen, il va tout simplement injecter mirage-tcpip (qui n’est fait qu’en OCaml). L’idée est de n’utiliser, du point de vue de l’application, qu’une interface (en l’occurrence mirage-stack) nous permettant de séparer la logique de l’application des autres fondements de notre système.

cohttp

Bien entendu, en tant que premier exemple réel d’un unikernel, il nous faut aussi une implémentation du protocole HTTP 1.1 : cohttp. Pour l’exemple, le site officiel de MirageOS est un unikernel utilisant cohttp. Mais ce projet, comme la plupart des projets MirageOS, dépasse l’écosystème et fait partie intégrante du plus large écosystème d’OCaml.

Puisque le développement d’une bibliothèque OCaml pour MirageOS se fait toujours en abstraction du système, spécialiser le cœur pour un système comme Linux ou Windows devient plus facile. La qualité la plus reconnue des projets Mirage est leur capacité à pouvoir être utilisés sur pratiquement tous les systèmes. Bien entendu, cette qualité est fortement liée à OCaml aussi qui propose un runtime s’exécutant nativement sur une multitude de plateformes.

ocaml-tls

La conception d’un système entier nécessite aussi de disposer de primitives de cryptographie, qui sont usuellement disponibles avec OpenSSL (ou l’un de ses forks). Il n’est, pour autant, pas aussi simple de ré-intégrer un code C existant (dépendant généralement des syscalls POSIX) dans MirageOS qui n’a, pour le coup, rien de toutes ces primitives.

Un effort colossal a donc été fait pour ré-implémenter les primitives de cryptographie essentielles afin de pouvoir ré-implémenter le protocole TLS, nécessaire pour servir un site internet accessible en HTTPS.

Là aussi, ocaml-tls est un projet phare limitant le prérequis d’instructions assembleur tout en proposant une bibliothèque avec des performances raisonnables. Encore une fois, il n’a absolument aucune notion de ce que peut être un socket ou de tout ce qui peut être POSIX-compliant.

ocaml-dns

Enfin, le domaine mirage.io est géré par un serveur primaire DNS qui est aussi un unikernel. Plusieurs services DNS tel qu’un résolveur et un service s’occupant du challenge DNS de let's encrypt sont disponibles grâce à ocaml-dns. Ce projet s’inscrit dans l’ambition de proposer des micro-services : un système d’exploitation pour un service spécifique.

Vous lancer dans l’écriture d’un unikernel

Des exemples prêts à compiler sont disponibles sur le github dont le classique hello world entièrement reproduit ici :

open Lwt.Infix

module Hello (Time : Mirage_time.S) = struct

  let start _time =

    let rec loop = function
      | 0 -> Lwt.return_unit
      | n ->
        Logs.info (fun f -> f "hello");
        Time.sleep_ns (Duration.of_sec 1) >>= fun () ->
        loop (n-1)
    in
    loop 4

end

Ce « noyau » se contente d’écrire quatre fois hello en 4 secondes et se termine. Si vous voulez expérimenter chez vous, vous devez mettre en place un environnement propice à la compilation et à l’exécution, par exemple avec les commandes suivantes (à adapter en fonction de votre distribution) :

sudo dnf install opam && \
opam init && opam update -yu && \
opam install mirage && eval $(opam env) && \
git clone https://github.com/mirage/mirage-skeleton && \
cd mirage-skeleton/tutorial/hello && \
mirage configure -t unix && make depend && make && \
./hello

(Dans cet exemple on produit un binaire exécutable mais en utilisant l’option de configuration mirage configure -t xen on peut par exemple produire un noyau utilisable avec l’hyperviseur Xen.)

Abstraction avec les functors

Comme vous pouvez le constater, un functor Time est utilisé pour générer la fonction start (qui est comme le main en C). C’est un module qui respect la signature Mirage_time.S. Grâce à ce module, nous pouvons utiliser la fonction sleep_ns. Son implémentation dépend bien entendu de la cible de votre MirageOS:
- pour UNIX, comme dans cette exemple, nous allons utiliser Unix.sleep
- pour Solo5 (KVM ou Xen), nous allons utiliser une fonction spécifique disponible dans mirage-solo5

Ce choix d’implémentation est fait par l’outil mirage, lorsque vous lancez: mirage configure. Dans ce cas, on prend l’implémentation par défaut proposée par mirage mais l’utilisateur peut très bien choisir son implémentation (tant qu'elle respecte la signature Mirage_time.S).

Autres aspects

Il y aurait encore beaucoup à dire, car cet article ne peut être exhaustif !
Vous êtes invités à le compléter dans vos commentaires.

Aller plus loin

  • # coquille(s)

    Posté par  . Évalué à 2 (+0/-0).

    Si la modération passe par là, il y a une étrange phrase à corriger :

    […] d’écrire quatre fois hello en 4quatresecondes et se termine.

    Merci pour cette dépêche intéressante.

    “It is seldom that liberty of any kind is lost all at once.” ― David Hume

  • # Intéressant

    Posté par  (site Web personnel) . Évalué à 6 (+6/-0).

    En lisant le titre, je m'attendais à une nouvelle distro basée sur le même principe que hurd, mais en fait, ça à l'air de se rapprocher plus d'un OS comme alpine pour faire des containers (prévenez-moi si je me trompe)…

    EFBBBFE297AFE2978EE297AF0D0AE297AFE297AFE2978E0D0AE2978EE2978EE2978E

    • [^] # Re: Intéressant

      Posté par  (site Web personnel) . Évalué à 10 (+16/-0).

      Il faut plutot le voir comme un cadriciel permettant de contruire "une application bootable". La librairie s’occupe de fournir les services bas-niveau (comme la pile tcp/ip présentée dans la dépeche) et permet de contruire une application dessus qui pourra etre exécutée dans un hyperviseur.

      Selon moi, le terme d’OS est inaproprié ici, car on s’est éloigné du logiciel permettant de lancer d’autres applications — encore que : cela est fait au moment de la compilation de l’image et non plus de l’exécution de l’OS comme on le retrouve habituellement. (si je voulais troller, je dirai que l’on pousse ici l’opposition entre langage interprété et langage compilé à l’extrème).

      Ça donne une idée de la quantité de travail nécessaire pour mettre en place la librairie, puisque le but est de fournir toutes les briques permettant de construire un système, non pas comme un gestionnaire de dépendance qui permet de construire son image, mais au sein d’une application unique.

  • # Une petite mise à jour

    Posté par  (site Web personnel) . Évalué à 10 (+12/-0). Dernière modification le 24/12/21 à 11:28.

    Déjà je voudrais remercier les contributeurs qui ont continué d'écrire cette dépêche sans moi et c'est super cool! Merci à eux!

    Ensuite, j'aimerais juste continuer un petit peu cette dépêche en parlant de la prochaine release de MirageOS 4. En effet, si je n'ai pas pu prendre le temps de bien écrire cette dépêche dans le détail, c'est entre autre parce que nous étions occupé à préparer cette grosse release qui n'est toujours pas fini.

    Cependant, elle risque d'arrivé bientôt puisque nous avons presque enfin toutes les pièces réunis pour continuer notre support avec libseccomp, KVM (gcloud) et Xen. La nouvelle release nous permet d'étendre assez facilement nos cibles et nous avons commencer à proposer un MirageOS pour Raspberry Pi 4 (en bare-metal) - cela reste un proof-of-concept mais il permet de montrer que tout fonctionne correctement. Vous pouvez voir le projet ici: https://github.com/dinosaure/gilbraltar (et j'espère qu'il sera bientôt intégré à MirageOS).

    Ensuite, il est mention dans cette dépêche de tout un tas de librairies pour MirageOS. Encore une fois, ces librairies peuvent être utilisés en dehors de MirageOS - et c'est le cas. En parallèle de la release de MirageOS 4, nous avons continué à maintenir ces librairies pour les utilisateurs. À cela, on peut noter plusieurs avancés:
    - ocaml-dns va bientôt intégrer le support de DNSSEC (la PR)
    - mirage-crypto utilises une version vérifié (à l'aide de Coq) de certains algorithmes de cryptographies (l'article)
    - nous avons développé tout une "stack" pour les emails (envoie, réception et vérification) et nous avons désormais un relais SMTP (avec signature DKIM et vérification SPF/DMARC) (le projet)
    - irmin continue d'évoluer avec la crypto-monnaie Tezos (irmin)
    - un remplacement de la stack HTTP vient de s'opéré étant plus performant (plus de détail ici: le project)
    - enfin, nous nous préparons pour une migration vers OCaml multicore
    - et nous avons développé un OS qui permet de programmer une Guirlande en OCaml pour notre sapin :) (la photo et la vidéo)

    Bref, il y a là un énorme travail de fond pour une petite équipe (~10 personnes). Mais il est certains que si vous voulez jouer avec, nous aurions un plaisir de vous aider!

    • [^] # Re: Une petite mise à jour

      Posté par  (site Web personnel) . Évalué à 6 (+3/-0).

      Ben ça fera l'objet de la prochaine dépêche :-)

      Merci pour cette mise à jour.

      « Tak ne veut pas quʼon pense à lui, il veut quʼon pense », Terry Pratchett, Déraillé.

    • [^] # Re: Une petite mise à jour

      Posté par  (site Web personnel) . Évalué à 4 (+2/-0).

      Merci pour les précisions !

      C’est super d’avoir une architecture physique comme le PI4 en cible ! Par contre, le pi4 est un peu overkill à mon niveau pour faire tourner MirageOS. Est-ce qu’il serait compliqué une fois cette première étape faite de l’étendre à d’autres modèles ? Le PI0v2 étant l’idéal (en terme de coût / consommation)

      C’est une bonne nouvelle également de passer sur httpaf plutôt que cohttp, qu’en est-il des webserver haut-niveau (opium / dream) ? Je vois qu’ils ne sont pas dans les paquets gérés sur le site de mirageOs, est-ce qu’il est possible de les compiler facilement dans l’architecture ?

      • [^] # Re: Une petite mise à jour

        Posté par  (site Web personnel) . Évalué à 5 (+4/-0).

        RPi4 est en effet un peu "overkill". En vérité, ce dernier est plus un "proof of concept" de l'extensibilité de MirageOS sur d'autres architecture physique (comme ESP32, RISC-V, etc.). Le travail nécessaire pour étendre MirageOS à une autre "target" (et pouvoir ré-utiliser les libraires OCaml plus haut niveau comme HTTP ou DNS) est certes compliqué mais minimal! Pour revenir à RPi4, il nous a fallut que 4 jours pour ajouter la "target" à MirageOS et 3 semaines pour programmer notre guirlande.

        Les questions derrière ce travail concerne notamment la problématique de la cross-compilation notamment si une librairie tierce embarque du code C. Bref, tout ces aspects devraient être résolu et si on articule bien la séparation entre le micro-micro-kernel et le domaine applicatif, on devrait pouvoir réutiliser le même code pour Raspberry Pi Zero.

        Pour ce qui est de Dream, nous avons proposé cet été DreamOS et nous continuons à essayer de supporter Dream. Le développement de ce dernier est plus ou moins actif mais il devient de plus en plus nécessaire pour nous puisque nous essayons de créer un serveur Matrix avec MirageOS + Dream. Bref, de ce côté là aussi ça bouge :) !

  • # Tous mes encouragements

    Posté par  . Évalué à 5 (+5/-0).

    C'est ce que j'ai lu de plus intéressant sur un OS en 2021. C'est une approche très agressive, mais qui offre la perspective d'OS minimaux adaptés à une tâche unique. Et après tout, pour un projet donné (genre un serveur web, un serveur d'appli avec une bdd; ou un firewall; ou un serveur DNS ou de mail; ou un noeud de minage) on sent bien qu'il y a un paquet de trucs dans l'OS qui ne servent à rien et qui sont donc potentiellement autant de trous béants. Après, une approche "ocam-only" risque de limiter sérieusement les personnes à qui cela s'adresse. D'ailleurs, lire le peu de code dans l'article provoque pour un néophyte un certain malaise ("c'est quoi ce code ?"). En tout cas chapeau à l'équipe technique, réécrire une pile tcp-ip, tls, … c'est un sacré challenge.

    Typiquement, pour un serveur de vote électronique, c'est le genre de techno qui serait intéressante.

    • [^] # Re: Tous mes encouragements

      Posté par  (site Web personnel) . Évalué à 3 (+1/-0).

      C'est vrai qu'Ocaml a l'air d'un truc de matheux, mais on peu faire beaucoup de choses dans ce langage sans creuser toutes les geekeries matheuses que les caméliste adorent tricoter.

      module type ORDERED = sig
        type t
      
        val compare : t -> t -> int
      end
      
      module Make (S : ORDERED) : sig
        type 'a t
      
        val empty : 'a t
        val add : S.t -> 'a -> 'a t -> 'a t
      end

      On pourrait traduire ça en Java dans le genre (je peux me tromper attention)

      interface ORDERED<T> {
      
      int compare(a: T, b : T);
      
      }
      
      interface Make<ORDERED<T>, U<V>> {
       U<T> empty;
       U<V> add(e : T, a : V, b : U<V>);
      }

      « Il n’y a pas de choix démocratiques contre les Traités européens » - Jean-Claude Junker

      • [^] # Re: Tous mes encouragements

        Posté par  . Évalué à 3 (+1/-0).

        Mis côte à côte, comme tu fais, il n'y a pas de quoi faire peur et au contraire OCaml a l'air sympa.

        Bon, quand on regarde en détail, je préfère

        int compare(a: T, b : T);

        (les fonctions façon LISP… pour compare et add dans cet exemple) à

        val compare : t -> t -> int

        Inversement, je préfère de loin

        module Make (S : ORDERED) : sig
          
        end

        à ce qui est de l'autre côté (exactement le même ressenti que j'ai avec Rust)

        interface Make<ORDERED<T>, U<V>> {
          
        }

        Dans mon cas, c'est l'abus de symboles (surtout quand ça prend des sens divers selon les contextes –comme là délimiteur et ailleurs opérateurs booléens…) …pourtant j'ai fait de l'APL (oui assez vieux pour ça) et du J ainsi que du PERL…

        “It is seldom that liberty of any kind is lost all at once.” ― David Hume

        • [^] # Re: Tous mes encouragements

          Posté par  . Évalué à 5 (+3/-0). Dernière modification le 31/12/21 à 00:23.

          Bon, quand on regarde en détail, je préfère

          int compare(a: T, b : T);

          (les fonctions façon LISP… pour compare et add dans cet exemple) à

          val compare : t -> t -> int

          C'est une question de point de vue, et je trouve la syntaxe OCaml bien plus cohérente quand on la regarde dans l'ensemble du langage.

          Une expression de la forme val e : t se lit « e est une valeur de type t », et je ne vois pas pourquoi il faudrait changer de forme, sous prétexte que la valeur compare est ici une fonction. Le : de OCaml c'est comme le epsilon (\epsilon) des mathématiciens, il signifie l'appartenance à un ensemble. Ici, c'est l'ensemble des fonctions des t vers les fonctions des t dans les int (ça se lit t -> (t -> int)), c'est une fonction qui renvoie une fonction. De même, la fonction successeur sur les entiers a pour type int -> int, c'est une fonction des int vers les int.

          En résumé, on a donc un nom pour la valeur, suivi du : (le epsilon des matheux, ou le est du français comme dans « Socrate est un homme ») puis enfin une expression qui appartient au langage des types.

          Cette dernière expression, on pourrait la réutiliser telle quelle dans le langage des types, pour définir par exemple un type paramétrique, comme ici :

          type 'a comparator = 'a -> 'a -> int

          Ici comparator est une fonction des types dans les types (un type paramétrique) et 'a est le nom de son paramètre. On peut alors réécrire la signature du début ainsi:

          (* la fonction `comparator` appliquée au type `t` *)
          val compare : t comparator (* t -> t -> int *)

          Il y a en fait 4 langages dans OCaml :

          • le langage des valeurs et expressions
          • le langage des types des expressions
          • le langage des modules
          • le langage des types de modules ou signatures

          Les deux derniers, dont tu préfères la syntaxe, ont « globalement » les même principe syntaxiques que les deux premiers qui semblent te désarçonner. Pourquoi ?

          Pour le foncteur Make, on aurait pu écrire sa signature ainsi :

          module Make : functor (S : ORDERED) -> sig ... end

          ce qui est la même syntaxe que pour compare, à part le mot clef functor (personnellement, je le trouve inutile et j'aurais préféré qu'il soit absent, je trouve qu'il alourdit sans raison l'écriture et la lecture).

          Sapere aude ! Aie le courage de te servir de ton propre entendement. Voilà la devise des Lumières.

          • [^] # Re: Tous mes encouragements

            Posté par  . Évalué à 3 (+1/-0). Dernière modification le 31/12/21 à 01:27.

            et je ne vois pas pourquoi il faudrait changer de forme, sous prétexte que la valeur compare est ici une fonction. Le : de OCaml c'est comme le epsilon (\epsilon) des mathématiciens, il signifie l'appartenance à un ensemble.

            Pas faux :-) Je suis influencé par l'habitude visuelle des autres langages que je pratique plus souvent.

            C'est une question de point de vue, et je trouve la syntaxe OCaml bien plus cohérente quand on la regarde dans l'ensemble du langage.

            Une fois qu'on remet le nez dedans, OCaml est en effet plus cohérent ; c'est juste qu'il me faut chaque fois un peu de temps. (pas encore parfaitement multilingue.)

            “It is seldom that liberty of any kind is lost all at once.” ― David Hume

          • [^] # Re: Tous mes encouragements

            Posté par  . Évalué à 1 (+1/-0).

            Je n'ai jamais aimé Caml à la fac, et ça continue.
            Cette syntaxe je ne la digère pas du tout, c'est juste incompréhensible pour mon esprit.

            Ton explication par exemple de :
            val compare : t -> (t -> int)

            Je me rappelle de mon prof qui disait qu'on pouvait le lire aussi:
            val compare : (t -> t) -> int

            Donc ça prend une fonction de t vers t et ça renvoie un int (qui de mon point de vue se rapproche davantage du "je prends 2 arguments et je renvoie un entier vrai/faux".
            À partir de là, s'il y a plusieurs niveaux de lecture, je laisse tomber.
            Ce langage est trop matheux à mon goût.
            Je ne pourrai jamais m'intéresser à un projet codé en (O)Caml, aussi intéressant puisse-t-il être.

            Mais merci pour ton explication, sans elle et son rappel de lecture, c'était le flou le plus total. Preuve que ce langage ne m'a pas marqué. Mais il suscite comme à l'époque une sorte d'énervement quand je le vois, je ne sais pas pourquoi …

Envoyer un commentaire

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n’en sommes pas responsables.