Ce billet a pour but de tester la coloration du code OCaml sur Aerie's Guard. En grandeur nature.
Certains d'entre vous ont expérimenté avec Haskell.
Certains d'entre vous ont expérimenté avec OCaml ou suivent des cours OCaml.
Peut-être que certains d'entre vous suivent des cours Caml-Light.
Quoiqu'il en soit, avant de me lancer dans le grand bain, j'essaye d'abord le petit bassin.
En OCaml un module peut être un module-fonction, c'est-à-dire qu'il peut être paramétrable par un (ou plusieurs) module-argument(s) d'un certain module-type(s).
Donc la 1ière chose que nous allons faire c'est déclarer un module-type.
Il s'agit du type des ensembles totalement ordonnés implémentés comme des arbres binaires de recherche.
Mais on ne précise pas s'ils sont mutables ou immutables (on le fera plus tard).
(* Totally ordered Sets *) module type OrderedSet = sig type 'a set = 'a non_empty_set option and 'a non_empty_set = private {mutable left: 'a set; item: 'a; mutable right: 'a set} val empty : 'a set val make : 'a set -> 'a -> 'a set -> 'a non_empty_set val with_left : 'a non_empty_set -> 'a set -> 'a non_empty_set val with_right : 'a non_empty_set -> 'a set -> 'a non_empty_set end
La 2ième chose c'est que l'on veut 2 modules-valeurs de ce module-type.
On veut un module-argument pour les ensembles totalement ordonnés immutables.
Spoiler (Sélectionnez le texte dans le cadre pointillé pour le faire apparaître)
(* Immutable totally ordered Sets *) module PureOrderedSet : OrderedSet = struct type 'a set = 'a non_empty_set option and 'a non_empty_set = {mutable left: 'a set; item: 'a; mutable right: 'a set} let empty = None let make l x r = {left=l; item=x; right=r} let with_left s l = {s with left=l} let with_right s r = {s with right=r} end
On veut un module-argument pour les ensembles totalement ordonnés mutables.
Spoiler (Sélectionnez le texte dans le cadre pointillé pour le faire apparaître)
(* Mutable totally ordered Sets *) module MutableOrderedSet : OrderedSet = struct type 'a set = 'a non_empty_set option and 'a non_empty_set = {mutable left: 'a set; item: 'a; mutable right: 'a set} let empty = None let make l x r = {left=l; item=x; right=r} let with_left s l = s.left <- l; s let with_right s r = s.right <- r; s end
La 3ième chose c'est que la récursion c'est compliqué. On peut se tromper et si on se trompe il faudra déboguer, soit parce que le calcul ne termine pas ou bien parce que la valeur retournée est incorrecte.
Déboguer c'est du temps perdu pour rien.
Alors on va régler le problème une fois pour toutes en encapsulant la récursion sur les ensembles totalement ordonnés.
Spoiler (Sélectionnez le texte dans le cadre pointillé pour le faire apparaître)
(* Recursing upon totally ordered Sets *) module OrderedSetRecursors(S:OrderedSet) = struct include S (* strict catamorphism *) type ('a,'b) fold = <empty: 'a; node: 'a -> 'b -> 'a -> 'a> let rec fold s (case:('a,'b) fold) = match s with | None -> case#empty | Some n -> case#node (fold n.left case) n.item (fold n.right case) (* lazy catamorphism *) type ('a,'b) cata = <empty: 'a; node: (unit -> 'a) -> 'b -> (unit -> 'a) -> 'a> let rec cata s (case:('a,'b) cata) () = match s with | None -> case#empty | Some n -> case#node (cata n.left case) n.item (cata n.right case) (* strict paramorphism *) type ('a,'b) recu = <empty: 'b; node: 'a non_empty_set -> 'b -> 'b -> 'b> let rec recu s (case:('a,'b) recu) = match s with | None -> case#empty | Some n -> case#node n (recu n.left case) (recu n.right case) (* lazy paramorphism *) type ('a,'b) para = <empty: 'b; node: 'a non_empty_set -> (unit -> 'b) -> (unit -> 'b) -> 'b> let rec para s (case:('a,'b) para) () = match s with | None -> case#empty | Some n -> case#node n (para n.left case) (para n.right case) let para_non_empty n (case:('a,'b) para) () = case#node n (para n.left case) (para n.right case) end
On veut une dernière chose :
Pouvoir créer des ensembles totalement ordonnés mutables ou immutables
Les équiper de toutes les opérations ensemblistes, à savoir e ∈ S, A ∪ B, A ∩ B, A ⊃ B, A - B, A = B, ∀ e ∈ S on a P(e), {e ∈ S, P(e)}
Que toutes ces opérations soient performantes, qu'elles utilisent le fait que les ensembles sont ordonnées. Par exemple il est hors de question d'ajouter les éléments de A un-par-un à l'ensemble B pour calculer A ∪ B.
Spoiler (Sélectionnez le texte dans le cadre pointillé pour le faire apparaître)
(* Build a concrete totally ordered Set *) module MakeSet(S:OrderedSet) = struct (* general *) include OrderedSetRecursors(S) let cardinal s = fold s ( object method empty = 0 method node l _ r = l + 1 + r end ) let for_all cond s = cata s ( object method empty = true method node l y r = l() && cond y && r() end ) () (* binary tree set *) let member x s = cata s ( object method empty = false method node l y r = if x < y then l() else if x > y then r() else true end ) () let insert x s = para s ( object method empty = make None x None method node n l r = let y = n.item in if x < y then with_left n (Some (l())) else if x > y then with_left n (Some (r())) else n end ) () let minimum s = para_non_empty s ( object method empty = s method node n l r = if n.left = empty then n else l() end ) () let remove_minimum s = para_non_empty s ( object method empty = empty method node n l r = if n.left = empty then n.right else Some (with_left n (l())) end ) () (* concatenation of sa + sb where max(sa) < min(sb) *) let concat sa sb = if sa = empty then sb else match sb with | None -> sa | Some n -> let m = minimum n and r = remove_minimum n in Some (with_right (with_left m sa) r) let remove x s = para s ( object method empty = empty method node n l r = let y = n.item in if x < y then Some (with_left n (l())) else if x > y then Some (with_right n (r())) else concat n.left n.right end ) () let split x s = para s ( object method empty = empty,false,empty method node n l r = let y = n.item in if x < y then let a,b,c = l() in a,b,Some (with_left n c) else if x > y then let a,b,c = r() in Some (with_right n a),b,c else n.left,true,n.right end ) () let filter cond s = recu s ( object method empty = empty method node n l r = if cond n.item then Some (with_right (with_left n l) r) else concat l r end ) let union sa sb = recu sa ( object method empty s = s method node m l r s = Some ( match s with | None -> m | Some n -> let a,b,c = split m.item s in with_right (with_left m (l a)) (r c)) end ) sb let intersection sa sb = recu sa ( object method empty s = empty method node m l r s = match s with | None -> empty | Some n -> let a,b,c = split m.item s in if b then Some (with_right (with_left m (l a)) (r c)) else concat (l a) (r c) end ) sb (* sa - sb *) let difference sa sb = recu sa ( object method empty s = empty method node m l r s = match s with | None -> Some m | Some n -> let a,b,c = split m.item s in if b then concat (l a) (r c) else Some (with_right (with_left m (l a)) (r c)) end ) sb let subset sa sb = recu sa ( object method empty s = true method node m l r s = match s with | None -> false | Some n -> if m.item < n.item then (l n.left) && member m.item n.left && (r s) else if m.item > n.item then (l s) && member m.item n.right && (r n.right) else (l n.left) && (r n.right) end ) sb let equal sa sb = subset sa sb && subset sb sa end
Désormais, pour créer un module ensemble totalement ordonné immutable :
module PSet = MakeSet(PureOrderedSet)
Désormais, pour créer un module ensemble totalement ordonné mutable :
module MSet = MakeSet(MutableOrderedSet)
Les fonctions ont toutes le type approprié. Par exemple il est impossible de retirer un élément d'un ensemble vide parce que la fonction remove est du type :
val remove : 'a -> 'a non_empty_set -> 'a set
Réciproquement, lorsque l'on insère un élément dans un ensemble quelconque, on obtient forcément un ensemble non vide :
val insert : 'a -> 'a set -> 'a non_empty_set
J'ai du mal à comprendre le fond de cet article, mais je constate que la coloration syntaxique marche bien, c'est déjà ça
Pourtant, dans la source finale il y a tout plein de object ... method ..., ça devrait t'être familier
Tout ça c'est la forme, si je ne comprends pas l'intérêt du problème, je peux très bien reconnaître des structures de langage, ça ne m'aide pas pour autant
Ce code est une petite partie de mon projet MoonLib.
Ne déroule que le dernier Spoiler.
Que vois-tu :
Il n'y a aucune boucle while ou for ou autre.
Il n'y a aucun let rec c'est-à-dire qu'il n'y a pas de récursion.
Mais alors, quel est le paradigme utilisé par ce code ?
Ce que tu vois c'est la réalisation de ce qu'Alain Prouté décrit dans les 13 premières minutes de son intervention à la conférence Innovaxiom :
Le paradigme utilisé n'est tout simplement pas présent dans la liste à droite de la page Wikipedia.
à 12m45s. Alain Prouté prononce les mots "c'est exactement le langage des Catégories Bicartésiennes Fermées". Ça n'est pas véritablement de la programmation fonctionnelle au sens où ça n'est pas Turing-complet. Tu peux considérer ça comme un nouveau paradigme à ajouter à la page Wikipedia.