diff --git a/src/kernel_services/abstract_interp/lattice_bounds.ml b/src/kernel_services/abstract_interp/lattice_bounds.ml
index 6ca6bc1eb377457d9fb5e89cab584196cc3a6f82..646ba8975ae5ac8eb5285c10c85f1b670ec7a63e 100644
--- a/src/kernel_services/abstract_interp/lattice_bounds.ml
+++ b/src/kernel_services/abstract_interp/lattice_bounds.ml
@@ -20,14 +20,19 @@
 (*                                                                        *)
 (**************************************************************************)
 
+
+
+(** Types definitions *)
+
 type 'a or_bottom = [ `Value of 'a | `Bottom ]
 type 'a or_top = [ `Value of 'a | `Top ]
 type 'a or_top_bottom = [ `Value of 'a | `Bottom | `Top ]
 
+
+
 (** Common functions *)
 
-module Common =
-struct
+module Common = struct
 
   (* Pretty-printing *)
 
@@ -82,19 +87,22 @@ struct
     | `Value vx, `Value vy -> is_included vx vy
 
   (* Iterator *)
+
   let iter f = function
     | `Bottom | `Top -> ()
     | `Value v -> f v
 
   (* Conversion *)
+
   let to_option = function
     | `Bottom | `Top -> None
     | `Value x -> Some x
 
 end
 
+
+
 module Bottom = struct
-  type 'a t = 'a or_bottom
 
   include Common
 
@@ -110,41 +118,38 @@ module Bottom = struct
 
   (* Lattice operators *)
 
-  let join join x y = match x, y with
-    | `Value vx, `Value vy    -> `Value (join vx vy)
+  let join join x y =
+    match x, y with
+    | `Value vx, `Value vy -> `Value (join vx vy)
     | `Bottom, (`Value _ as v)
     | (`Value _ as v), `Bottom
     | (`Bottom as v), `Bottom -> v
 
-  let join_list j l = List.fold_left (join j) `Bottom l
-
-  let narrow narrow x y = match x, y with
-    | `Value vx, `Value vy    -> narrow vx vy
+  let narrow narrow x y =
+    match x, y with
+    | `Value vx, `Value vy -> narrow vx vy
     | `Bottom, `Value _
     | `Value _, `Bottom
     | `Bottom, `Bottom -> `Bottom
 
-  (* Iterators *)
-
-  let bind f = function
-    | `Bottom -> `Bottom
-    | `Value v -> f v
-
-  let fold ~bottom f = function
-    | `Bottom -> bottom
-    | `Value v -> f v
-
-  let map f = function
-    | `Bottom -> `Bottom
-    | `Value v -> `Value (f v)
+  let join_list f = List.fold_left (join f) `Bottom
 
   (* Combination *)
 
   let zip x y =
     match x, y with
-    | `Bottom, #t | #t, `Bottom -> `Bottom
+    | `Bottom, _ | _, `Bottom -> `Bottom
     | `Value x, `Value y -> `Value (x,y)
 
+  (* Monadic operations *)
+
+  include Monad.Extend_Kleisli_with_product (struct
+      type 'a t = 'a or_bottom
+      let return x = `Value x
+      let bind f = function `Bottom -> `Bottom | `Value x -> f x
+      let product l r = zip l r
+    end)
+
   (** Conversion *)
 
   let of_option = function
@@ -155,41 +160,19 @@ module Bottom = struct
     | `Bottom  -> []
     | `Value v -> [v]
 
-  let bot_of_list = function
-    | [] -> `Bottom
-    | l  -> `Value l
-
-  let list_of_bot = function
-    | `Bottom  -> []
-    | `Value l -> l
-
-  let add_to_list elt list = match elt with
+  let add_to_list elt list =
+    match elt with
     | `Bottom    -> list
     | `Value elt -> elt :: list
 
   let list_values l =
     List.fold_left (fun l elt -> add_to_list elt l) [] l
 
-  (** Operators *)
-
-  module Operators =
-  struct
-    let (>>-) x f = bind f x
-    let (>>-:) x f = map f x
-    let (let+) x f = map f x
-    let (and+) = zip
-    let (let*) x f = bind f x
-    let (and*) = zip
-  end
-
   (** Datatype construction *)
+
   let counter = ref 0
 
-  module Make_Datatype
-      (Domain: Datatype.S)
-    =
-    Datatype.Make (
-    struct
+  module Make_Datatype (Domain: Datatype.S) = Datatype.Make (struct
       include Datatype.Serializable_undefined
       type t = Domain.t or_bottom
       let () = incr counter
@@ -207,21 +190,18 @@ module Bottom = struct
 
   (* Bound lattice *)
 
-  module Bound_Lattice
-      (Lattice: Lattice_type.Join_Semi_Lattice)
-  = struct
+  module Bound_Lattice (Lattice: Lattice_type.Join_Semi_Lattice) = struct
     include Make_Datatype (Lattice)
-
     let bottom = `Bottom
     let join = join Lattice.join
     let is_included = is_included Lattice.is_included
   end
+
 end
 
 
-module Top =
-struct
-  type 'a t = 'a or_top
+
+module Top = struct
 
   include Common
 
@@ -235,6 +215,12 @@ struct
     | `Value v -> v
     | `Top -> top
 
+  (** Conversion. *)
+
+  let of_option = function
+    | None -> `Top
+    | Some x -> `Value x
+
   (** Lattice *)
 
   let join join_value x y =
@@ -247,52 +233,88 @@ struct
     | `Top, v | v, `Top -> v
     | `Value vx, `Value vy -> `Value (narrow_value vx vy)
 
-  (* Iterators *)
+  (** Combination *)
 
-  let bind f = function
-    | `Top -> `Top
-    | `Value v -> f v
+  let zip x y =
+    match x, y with
+    | `Top, _ | _, `Top -> `Top
+    | `Value x, `Value y -> `Value (x,y)
 
-  let fold ~top f = function
-    | `Top -> top
-    | `Value v -> f v
+  (** Monadic operators *)
 
-  let map f = function
-    | `Top -> `Top
-    | `Value v -> `Value (f v)
+  include Monad.Extend_Kleisli_with_product (struct
+      type 'a t = 'a or_top
+      let return x = `Value x
+      let bind f = function `Top -> `Top | `Value x -> f x
+      let product l r = zip l r
+    end)
+
+  (** Datatype construction *)
+
+  let counter = ref 0
+
+  module Make_Datatype (Domain: Datatype.S) = Datatype.Make (struct
+      include Datatype.Serializable_undefined
+      type t = Domain.t or_top
+      let () = incr counter
+      let name = Domain.name ^ "+top(" ^ string_of_int !counter ^ ")"
+      let reprs = `Top :: (List.map (fun v -> `Value v) Domain.reprs)
+      let structural_descr = Structural_descr.t_unknown
+      let hash = Common.hash Domain.hash
+      let equal = (Common.equal Domain.equal :> t -> t -> bool)
+      let compare = Common.compare Domain.compare
+      let rehash = Datatype.identity
+      let copy = map Domain.copy
+      let pretty = Common.pretty Domain.pretty
+      let mem_project = Datatype.never_any_project
+    end)
+
+end
+
+
+
+module TopBottom = struct
+
+  type 'a t = 'a or_top_bottom
+  include Common
 
   (** Combination *)
 
   let zip x y =
     match x, y with
+    | `Bottom, #t | #t, `Bottom -> `Bottom
     | `Top, #t | #t, `Top -> `Top
     | `Value x, `Value y -> `Value (x,y)
 
-  (** Conversion. *)
+  (** Monadic operators. We have to redefines every operators to ensure
+      subtyping properties. *)
 
-  let of_option = function
-    | None -> `Top
-    | Some x -> `Value x
+  let return x = `Value x
+  let product l r = zip l r
 
-  (** Operators *)
+  let bind f = function
+    | `Bottom -> `Bottom
+    | `Top -> `Top
+    | `Value x -> f x
 
-  module Operators =
-  struct
-    let (>>-) x f = bind f x
-    let (>>-:) x f = map f x
-    let (let+) x f = map f x
-    let (and+) = zip
-    let (let*) x f = bind f x
-    let (and*) = zip
+  let map f = function
+    | `Bottom -> `Bottom
+    | `Top -> `Top
+    | `Value x -> `Value (f x)
+
+  let flatten = function
+    | `Bottom | `Value `Bottom -> `Bottom
+    | `Top | `Value `Top -> `Top
+    | `Value `Value x -> `Value x
+
+  module Operators = struct
+    let ( >>-  ) (m : [< 'a t]) (f : 'a -> ([> 'b t] as 'c)) : 'c = bind f m
+    let ( let* ) (m : [< 'a t]) (f : 'a -> ([> 'b t] as 'c)) : 'c = bind f m
+    let ( and* ) (l : [< 'a t]) (r : [< 'b t]) : [> ('a * 'b) t] = product l r
+    let ( >>-: ) (m : [< 'a t]) (f : 'a -> 'b) : [> 'b t] = map f m
+    let ( let+ ) (m : [< 'a t]) (f : 'a -> 'b) : [> 'b t] = map f m
+    let ( and+ ) (l : [< 'a t]) (r : [< 'b t]) : [> ('a * 'b) t] = product l r
   end
-end
-
-
-module TopBottom =
-struct
-  type 'a t = 'a or_top_bottom
-
-  include Common
 
   (* Lattice operators *)
 
@@ -306,40 +328,24 @@ struct
     | `Bottom, _ | _, `Bottom -> `Bottom
     | `Value vx, `Value vy -> (narrow_value vx vy :> 'a t)
 
-  (* Iterators *)
-
-  let bind f = function
-    | `Top -> `Top
-    | `Bottom -> `Bottom
-    | `Value v -> f v
-
-  let fold ~top ~bottom f = function
-    | `Top -> top
-    | `Bottom -> bottom
-    | `Value v -> f v
-
-  let map f = function
-    | `Top -> `Top
-    | `Bottom -> `Bottom
-    | `Value v -> `Value (f v)
-
-  (** Combination *)
+  (** Datatype construction *)
 
-  let zip x y =
-    match x, y with
-    | `Bottom, #t | #t, `Bottom -> `Bottom
-    | `Top, #t | #t, `Top -> `Top
-    | `Value x, `Value y -> `Value (x,y)
+  let counter = ref 0
 
-  (** Operators *)
+  module Make_Datatype (Domain: Datatype.S) = Datatype.Make (struct
+      include Datatype.Serializable_undefined
+      type t = Domain.t or_top_bottom
+      let () = incr counter
+      let name = Domain.name ^ "+top_bottom(" ^ string_of_int !counter ^ ")"
+      let reprs = `Bottom :: `Top :: (List.map (fun v -> `Value v) Domain.reprs)
+      let structural_descr = Structural_descr.t_unknown
+      let hash = Common.hash Domain.hash
+      let equal = (Common.equal Domain.equal :> t -> t -> bool)
+      let compare = Common.compare Domain.compare
+      let rehash = Datatype.identity
+      let copy = map Domain.copy
+      let pretty = Common.pretty Domain.pretty
+      let mem_project = Datatype.never_any_project
+    end)
 
-  module Operators =
-  struct
-    let (>>-) x f = bind f x
-    let (>>-:) x f = map f x
-    let (let+) x f = map f x
-    let (and+) = zip
-    let (let*) x f = bind f x
-    let (and*) = zip
-  end
 end
diff --git a/src/kernel_services/abstract_interp/lattice_bounds.mli b/src/kernel_services/abstract_interp/lattice_bounds.mli
index 903fec7a1db3058103e5c6156cffcb759e4af295..9b444d32611eef76632a75e2d37034436c38384a 100644
--- a/src/kernel_services/abstract_interp/lattice_bounds.mli
+++ b/src/kernel_services/abstract_interp/lattice_bounds.mli
@@ -29,163 +29,129 @@ type 'a or_top_bottom = [ `Value of 'a | `Bottom | `Top ]
 
 
 module Bottom : sig
-  type 'a t = 'a or_bottom
 
-  (** Operators *)
-  module Operators : sig
-    (** This monad propagates `Bottom and or `Top if needed. *)
-    val (>>-) : [< 'a t] -> ('a -> ([> 'b t] as 'c)) -> 'c
-
-    (** Use this monad if the following function returns a simple value. *)
-    val (>>-:) : [< 'a t] -> ('a -> 'b) -> [> 'b t]
-
-    (* Binding operators, applicative syntax *)
-    val (let+) : [< 'a t] -> ('a -> 'b) -> [> 'b t]
-    val (and+) : [< 'a t] -> [< 'b t] -> [> ('a * 'b) t]
-
-    (* Binding operators, monad syntax *)
-    val (let*) : [< 'a t] -> ('a -> ([> 'b t] as 'c)) -> 'c
-    val (and*) : [< 'a t] -> [< 'b t] -> [> ('a * 'b) t]
-  end
+  include Monad.S_with_product with type 'a t = 'a or_bottom
 
   (** Datatype constructor *)
-  module Make_Datatype
-      (Domain: Datatype.S)
-    : Datatype.S with type t = Domain.t or_bottom
+  module Make_Datatype (Domain: Datatype.S) :
+    Datatype.S with type t = Domain.t or_bottom
 
   (** Bounds a semi-lattice *)
-  module Bound_Lattice
-      (Lattice: Lattice_type.Join_Semi_Lattice)
-    : Lattice_type.Bounded_Join_Semi_Lattice with type t = Lattice.t or_bottom
+  module Bound_Lattice (Lattice: Lattice_type.Join_Semi_Lattice) :
+    Lattice_type.Bounded_Join_Semi_Lattice with type t = Lattice.t or_bottom
 
   (** Access *)
-  val is_bottom: 'a t -> bool
-  val non_bottom: 'a t -> 'a
-  val value: bottom: 'a -> 'a t -> 'a
+  val is_bottom : 'a t -> bool
+  val non_bottom : 'a t -> 'a
+  val value : bottom:'a -> 'a t -> 'a
 
   (** Datatype *)
-  val hash: ('a -> int) -> 'a t -> int
-  val equal: ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
-  val compare: ('a -> 'a -> int)  -> 'a t -> 'a t -> int
+  val hash : ('a -> int) -> 'a t -> int
+  val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
+  val compare : ('a -> 'a -> int)  -> 'a t -> 'a t -> int
 
   (** Pretty-printing *)
-  val pretty_bottom :Format.formatter -> unit (* for %t specifier *)
-  val pretty :
-    (Format.formatter -> 'a -> unit) ->
-    Format.formatter -> 'a t -> unit
+  val pretty_bottom : Format.formatter -> unit (* for %t specifier *)
+  val pretty : (Format.formatter -> 'a -> unit) -> Format.formatter -> 'a t -> unit
 
   (* Lattice operators *)
-  val is_included: ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
-  val join: ('a -> 'a -> 'a) -> 'a t -> 'a t -> 'a t
-  val join_list: ('a -> 'a -> 'a) -> 'a t list -> 'a t
-  val narrow: ('a -> 'a -> 'a t) -> 'a t -> 'a t -> 'a t
+  val is_included : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
+  val join : ('a -> 'a -> 'a) -> 'a t -> 'a t -> 'a t
+  val narrow : ('a -> 'a -> 'a t) -> 'a t -> 'a t -> 'a t
+  val join_list : ('a -> 'a -> 'a) -> 'a t list -> 'a t
 
   (* Iterators *)
-  val iter: ('a -> unit) -> 'a t -> unit
-  val bind: ('a -> 'b t) -> 'a t -> 'b t
-  val fold: bottom:'b -> ('a -> 'b) -> 'a t -> 'b
-  val map: ('a -> 'b) -> 'a t -> 'b t
+  val iter : ('a -> unit) -> 'a t -> unit
 
   (* Combination *)
-  val zip: 'a t -> 'b t -> ('a * 'b) t (* `Bottom if any is `Bottom *)
+  val zip : 'a t -> 'b t -> ('a * 'b) t (* `Bottom if any is `Bottom *)
 
   (** In a lattice where the elements are lists of non-bottom values,
       the empty list is the bottom case. *)
 
   (** Conversion *)
-  val to_option: 'a t -> 'a option
-  val of_option: 'a option -> 'a t
-  val to_list: 'a t -> 'a list
-  val bot_of_list: 'a list -> 'a list t
-  val list_of_bot: 'a list t -> 'a list
-  val list_values: 'a t list -> 'a list
+  val to_option : 'a t -> 'a option
+  val of_option : 'a option -> 'a t
+  val to_list : 'a t -> 'a list
+  val list_values : 'a t list -> 'a list
 
   (** [elt >:: list] adds [elt] to the [list] if it is not bottom. *)
-  val add_to_list: 'a t -> 'a list -> 'a list
+  val add_to_list : 'a t -> 'a list -> 'a list
+
 end
 
 
 module Top : sig
-  type 'a t = 'a or_top
 
-  (** Operators *)
-  module Operators : sig
-    (** This monad propagates `Bottom and or `Top if needed. *)
-    val (>>-) : [< 'a t] -> ('a -> ([> 'b t] as 'c)) -> 'c
+  include Monad.S_with_product with type 'a t = 'a or_top
 
-    (** Use this monad if the following function returns a simple value. *)
-    val (>>-:) : [< 'a t] -> ('a -> 'b) -> [> 'b t]
-
-    (* Binding operators, applicative syntax *)
-    val (let+) : [< 'a t] -> ('a -> 'b) -> [> 'b t]
-    val (and+) : [< 'a t] -> [< 'b t] -> [> ('a * 'b) t]
-
-    (* Binding operators, monad syntax *)
-    val (let*) : [< 'a t] -> ('a -> ([> 'b t] as 'c)) -> 'c
-    val (and*) : [< 'a t] -> [< 'b t] -> [> ('a * 'b) t]
-  end
+  (** Datatype constructor *)
+  module Make_Datatype (Domain: Datatype.S) :
+    Datatype.S with type t = Domain.t or_top
 
   (** Access *)
-  val is_top: 'a t -> bool
-  val non_top: 'a t -> 'a
-  val value: top: 'a -> 'a t -> 'a
+  val is_top : 'a t -> bool
+  val non_top : 'a t -> 'a
+  val value : top:'a -> 'a t -> 'a
 
   (** Datatype *)
-  val hash: ('a -> int) -> 'a t -> int
-  val equal: ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
-  val compare: ('a -> 'a -> int)  -> 'a t -> 'a t -> int
+  val hash : ('a -> int) -> 'a t -> int
+  val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
+  val compare : ('a -> 'a -> int)  -> 'a t -> 'a t -> int
 
   (** Pretty-printing *)
-  val pretty_top :Format.formatter -> unit (* for %t specifier *)
+  val pretty_top : Format.formatter -> unit (* for %t specifier *)
   val pretty :
     (Format.formatter -> 'a -> unit) ->
     Format.formatter -> 'a t -> unit
 
   (** Lattice operators *)
-  val join: ('a -> 'a -> 'a t) -> 'a t -> 'a t -> 'a t
-  val narrow: ('a -> 'a -> 'a) -> 'a t -> 'a t -> 'a t
+  val is_included : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
+  val join : ('a -> 'a -> 'a t) -> 'a t -> 'a t -> 'a t
+  val narrow : ('a -> 'a -> 'a) -> 'a t -> 'a t -> 'a t
 
   (* Iterators *)
-  val iter: ('a -> unit) -> 'a t -> unit
-  val bind: ('a -> 'b t) -> 'a t -> 'b t
-  val fold: top:'b -> ('a -> 'b) -> 'a t -> 'b
-  val map: ('a -> 'b) -> 'a t -> 'b t
+  val iter : ('a -> unit) -> 'a t -> unit
 
   (** Combination *)
-  val zip: 'a t -> 'b t -> ('a * 'b) t (* `Top if any is `Top *)
+  val zip : 'a t -> 'b t -> ('a * 'b) t (* `Top if any is `Top *)
 
   (** Conversion *)
   val to_option : 'a t -> 'a option
   val of_option : 'a option -> 'a t
+
 end
 
 
 module TopBottom: sig
-  type 'a t = 'a or_top_bottom
 
-  (** Operators *)
-  (* In presence of simultaneous `Bottom and `Top in and+ / and*, everything
-     narrows down to `Bottom *)
-  module Operators : sig
-    (** This monad propagates `Bottom and or `Top if needed. *)
-    val (>>-) : [< 'a t] -> ('a -> ([> 'b t] as 'c)) -> 'c
+  include Monad.S_with_product with type 'a t = 'a or_top_bottom
 
-    (** Use this monad if the following function returns a simple value. *)
-    val (>>-:) : [< 'a t] -> ('a -> 'b) -> [> 'b t]
+  (** Datatype constructor *)
+  module Make_Datatype (Domain: Datatype.S) :
+    Datatype.S with type t = Domain.t or_top_bottom
+
+  (** Operators. In presence of simultaneous `Bottom and `Top in and+ / and*,
+      everything narrows down to `Bottom. Every operators is redefined to
+      ensure subtyping properties. *)
+  module Operators : sig
 
     (* Binding operators, applicative syntax *)
+    val (>>-:) : [< 'a t] -> ('a -> 'b) -> [> 'b t]
     val (let+) : [< 'a t] -> ('a -> 'b) -> [> 'b t]
     val (and+) : [< 'a t] -> [< 'b t] -> [> ('a * 'b) t]
 
     (* Binding operators, monad syntax *)
+    val (>>- ) : [< 'a t] -> ('a -> ([> 'b t] as 'c)) -> 'c
     val (let*) : [< 'a t] -> ('a -> ([> 'b t] as 'c)) -> 'c
     val (and*) : [< 'a t] -> [< 'b t] -> [> ('a * 'b) t]
+
   end
 
   (** Datatype *)
-  val hash: ('a -> int) -> 'a t -> int
-  val equal: ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
-  val compare: ('a -> 'a -> int)  -> 'a t -> 'a t -> int
+  val hash : ('a -> int) -> 'a t -> int
+  val equal : ('a -> 'a -> bool) -> 'a t -> 'a t -> bool
+  val compare : ('a -> 'a -> int)  -> 'a t -> 'a t -> int
 
   (** Pretty-printing *)
   val pretty :
@@ -193,12 +159,8 @@ module TopBottom: sig
     Format.formatter -> 'a t -> unit
 
   (* Lattice operators *)
-  val join: ('a -> 'a -> [< 'a t]) -> 'a t -> 'a t -> 'a t
-  val narrow: ('a -> 'a -> [< 'a t]) -> 'a t -> 'a t -> 'a t
+  val is_included : ('a -> 'b -> bool) -> 'a t -> 'b t -> bool
+  val join : ('a -> 'a -> [< 'a t]) -> 'a t -> 'a t -> 'a t
+  val narrow : ('a -> 'a -> [< 'a t]) -> 'a t -> 'a t -> 'a t
 
-  (* Iterators *)
-  val iter: ('a -> unit) -> 'a t -> unit
-  val bind: ('a -> 'b t) -> 'a t -> 'b t
-  val fold: top:'b -> bottom:'b -> ('a -> 'b) -> 'a t -> 'b
-  val map: ('a -> 'b) -> 'a t -> 'b t
 end
diff --git a/src/libraries/monads/monad.ml b/src/libraries/monads/monad.ml
new file mode 100644
index 0000000000000000000000000000000000000000..61099357703f0429a7066e09247dbaf5ca82f980
--- /dev/null
+++ b/src/libraries/monads/monad.ml
@@ -0,0 +1,157 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+
+
+(* Kleisli triple minimal signature *)
+module type Kleisli = sig
+  type 'a t
+  val return : 'a -> 'a t
+  val bind : ('a -> 'b t) -> 'a t -> 'b t
+end
+
+(* Categoric minimal signature *)
+module type Categoric = sig
+  type 'a t
+  val return : 'a -> 'a t
+  val map : ('a -> 'b) -> 'a t -> 'b t
+  val flatten : 'a t t -> 'a t
+end
+
+(* Complete signature *)
+module type S = sig
+  type 'a t
+  val return : 'a -> 'a t
+  val flatten : 'a t t -> 'a t
+  val map  : ('a -> 'b  ) -> 'a t -> 'b t
+  val bind : ('a -> 'b t) -> 'a t -> 'b t
+  module Operators : sig
+    val ( >>-  ) : 'a t -> ('a -> 'b t) -> 'b t
+    val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
+    val ( >>-: ) : 'a t -> ('a -> 'b) -> 'b t
+    val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
+  end
+end
+
+(** Extend a Kleisli triple monad *)
+module Extend_Kleisli (M : Kleisli) = struct
+  type 'a t = 'a M.t
+  let return x = M.return x
+  let bind f m = M.bind f m
+  let flatten m = bind (fun x -> x) m
+  let map f m = bind (fun x -> return (f x)) m
+  module Operators = struct
+    let ( >>-  ) m f = bind f m
+    let ( let* ) m f = bind f m
+    let ( >>-: ) m f = map  f m
+    let ( let+ ) m f = map  f m
+  end
+end
+
+(** Extend a categoric monad *)
+module Extend_Categoric (M : Categoric) = struct
+  type 'a t = 'a M.t
+  let return x = M.return x
+  let map f m = M.map f m
+  let flatten m = M.flatten m
+  let bind f m = flatten (map f m)
+  module Operators = struct
+    let ( >>-  ) m f = bind f m
+    let ( let* ) m f = bind f m
+    let ( >>-: ) m f = map  f m
+    let ( let+ ) m f = map  f m
+  end
+end
+
+
+
+(* Product on monads *)
+module type Product = sig
+  type 'a t
+  val product : 'a t -> 'b t -> ('a * 'b) t
+end
+
+(* Kleisli triple with a product *)
+module type Kleisli_with_product = sig
+  include Kleisli
+  include Product with type 'a t := 'a t
+end
+
+(* Categoric monad with a product *)
+module type Categoric_with_product = sig
+  include Categoric
+  include Product with type 'a t := 'a t
+end
+
+(* Complete signature with a product *)
+module type S_with_product = sig
+  type 'a t
+  val return : 'a -> 'a t
+  val flatten : 'a t t -> 'a t
+  val map  : ('a -> 'b  ) -> 'a t -> 'b t
+  val bind : ('a -> 'b t) -> 'a t -> 'b t
+  val product : 'a t -> 'b t -> ('a * 'b) t
+  module Operators : sig
+    val ( >>-  ) : 'a t -> ('a -> 'b t) -> 'b t
+    val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
+    val ( and* ) : 'a t -> 'b t -> ('a * 'b) t
+    val ( >>-: ) : 'a t -> ('a -> 'b) -> 'b t
+    val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
+    val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t
+  end
+end
+
+(** Extend a Kleisli triple monad with a product *)
+module Extend_Kleisli_with_product (M : Kleisli_with_product) = struct
+  type 'a t = 'a M.t
+  let return x = M.return x
+  let bind f m = M.bind f m
+  let flatten m = bind (fun x -> x) m
+  let map f m = bind (fun x -> return (f x)) m
+  let product l r = M.product l r
+  module Operators = struct
+    let ( >>-  ) m f = bind f m
+    let ( let* ) m f = bind f m
+    let ( let+ ) m f = map  f m
+    let ( >>-: ) m f = map  f m
+    let ( and* ) l r = product l r
+    let ( and+ ) l r = product l r
+  end
+end
+
+(** Extend a categoric monad with a product *)
+module Extend_Categoric_with_product (M : Categoric_with_product) = struct
+  type 'a t = 'a M.t
+  let return x = M.return x
+  let map f m = M.map f m
+  let flatten m = M.flatten m
+  let bind f m = flatten (map f m)
+  let product l r = M.product l r
+  module Operators = struct
+    let ( >>-  ) m f = bind f m
+    let ( let* ) m f = bind f m
+    let ( let+ ) m f = map  f m
+    let ( >>-: ) m f = map  f m
+    let ( and* ) l r = product l r
+    let ( and+ ) l r = product l r
+  end
+end
diff --git a/src/libraries/monads/monad.mli b/src/libraries/monads/monad.mli
new file mode 100644
index 0000000000000000000000000000000000000000..a31294b6e49cdab9f3ac85c4ad58a418bc8f3eb9
--- /dev/null
+++ b/src/libraries/monads/monad.mli
@@ -0,0 +1,264 @@
+(**************************************************************************)
+(*                                                                        *)
+(*  This file is part of Frama-C.                                         *)
+(*                                                                        *)
+(*  Copyright (C) 2007-2024                                               *)
+(*    CEA (Commissariat à l'énergie atomique et aux énergies              *)
+(*         alternatives)                                                  *)
+(*                                                                        *)
+(*  you can redistribute it and/or modify it under the terms of the GNU   *)
+(*  Lesser General Public License as published by the Free Software       *)
+(*  Foundation, version 2.1.                                              *)
+(*                                                                        *)
+(*  It is distributed in the hope that it will be useful,                 *)
+(*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *)
+(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *)
+(*  GNU Lesser General Public License for more details.                   *)
+(*                                                                        *)
+(*  See the GNU Lesser General Public License version 2.1                 *)
+(*  for more details (enclosed in the file licenses/LGPLv2.1).            *)
+(*                                                                        *)
+(**************************************************************************)
+
+
+
+(** {2 Kleisli triple signature for a monadic type constructor ['a t]}
+
+    This is the usual minimal definition of a monadic type constructor.
+    The [return] function embeds a value {m x} in the monad. The [bind]
+    function, also called the "combinator", corresponds to the idea of
+    "sequence" in the monadic world, i.e the call [bind f m] comes down
+    to performing the computation [m] before applying the next computation
+    step, represented by the function [f], to the resulting value.
+
+    To fully qualify as a monad, these three parts must respect the three
+    following laws :
+
+    1. [return] is a left-identity for [bind] :
+       ∀f:('a -> 'b t), ∀x:'a, [bind f (return x) ≣ return (f x)]
+
+    2. [return] is a right-identity for [bind] :
+       ∀m:'a t, [bind return m ≣ m]
+
+    3. [bind] is associative :
+       ∀m:'a t, ∀f:('a -> 'b t), ∀g:('b -> 'c t),
+       [bind (fun x -> bind g (f x)) m ≣ bind g (bind f m)]
+
+    As there is no way in the OCaml type system to enforce those properties,
+    users have to trust the implemented monad when using it, and developpers
+    have to manually check that they are respected. *)
+module type Kleisli = sig
+  type 'a t
+  val return : 'a -> 'a t
+  val bind : ('a -> 'b t) -> 'a t -> 'b t
+end
+
+
+
+(** {2 Categoric signature for a monadic type constructor ['a t]}
+
+    In a computer science context, this is a rarer definition of a monadic
+    type constructor. It is however equivalent to the Kleisli triple one,
+    and may be simpler to use for certain monads, such as the [list] one.
+
+    To be pedantic, this approach defines a monad as a categoric functor
+    equipped with two natural transformations. This does sounds frightening
+    but this breaks down to rather simple concepts.
+
+    Let's start at the beginning. A category is just a collection of objets
+    and arrows (or morphisms) between those objets that satisfies two
+    properties: there exists a morphism from all objects to themselves, i.e
+    an identity, and if there is a morphism between objects [a] and [b] and
+    a morphism between objects [b] and [c], then there must be a morphism
+    between [a] and [c], i.e morphisms are associative.
+
+    There is a strong parallel between categories and type systems. Indeed,
+    if one uses the collection of all types as objects, then for all types
+    ['a] and ['b], the function [f : 'a -> 'b] can be seen as a morphism
+    between the objets ['a] and ['b]. As functions are naturally associative
+    and, for any type ['a], one can trivially defines the identity function
+    ['a -> 'a], one can conclude that types along with all functions of
+    arithy one forms a category.
+
+    Next, there is the idea of functors. In the category theory, a functor
+    is a mapping between categories. That means that, given two categories
+    [A] and [B], a functor maps all objects of [A] to an object of [B] and
+    maps all morphisms of [A] into a morphims of [B]. But, not all mappings
+    are functors. Indeed, to be a valid functor, one as to preserve the
+    identity morphisms and the composition of morphims.
+
+    The idea of functors can also be seen in a type systems. At least, the
+    more restricted but enough here of an endofunctor, a functor from a
+    category to itself. Indeed, for any type [v] and for any parametric
+    type ['a t], the type [v t] can be seen as a mapping from values of
+    type [v] to values of type [v t]. Thus the type ['a t] encodes the first
+    part of what a functor is, a mapping from objects of the Type category
+    to objects of the Type category. For the second part, we need a way to
+    transform morphisms of the Type category, i.e functions of type ['a -> 'b],
+    in a way that preserves categoric properties. Expressed as a type, this
+    transformation can be seen as a higher-order function [map] with the
+    type [map : ('a -> 'b) -> ('a t -> 'b t)].
+
+    Finally, this definition of a monad requires two natural transformations.
+    Simply put, a natural transformation is a mapping between functors that
+    preserves the structures of all the underlying categories (mathematicians
+    and naming conventions...). That may be quite complicated to grasp, but in
+    this case, the two required transformations are quite simple and intuitive.
+
+    The first one, as in a Kleisli triple, is a [return] function that embeds
+    values {m x} of type [v] into the monad. This function must satisfies
+    similar laws than in a Kleisli triple, but expressed in terms of [map] :
+
+    1. ∀x:'a, ∀f:('a -> 'b), [return (f x) ≣ map f (return x)]
+
+    The second one is called [flatten], and can be seen as a way to "flatten"
+    nested applications of the monad. It must also satisfy some laws :
+
+    2. ∀m:('a t t t), [map flatten (flatten m) ≣ flatten (flatten m)]
+    3. ∀m:('a t), [flatten (map return m) ≣ flatten (return m) ≣ m]
+    4. ∀m:('a t t), ∀f: ('a -> 'b), [flatten (map (map f) m) ≣ map f (flatten m)]
+
+    As there is no way in the OCaml type system to enforce those properties,
+    users have to trust the implemented monad when using it, and developpers
+    have to manually check that they are respected. *)
+module type Categoric = sig
+  type 'a t
+  val return : 'a -> 'a t
+  val map : ('a -> 'b) -> 'a t -> 'b t
+  val flatten : 'a t t -> 'a t
+end
+
+
+
+(** {2 Extended signature with let-bindings}
+
+    The minimal definitions of a monad are not that useful when one tries to
+    develop clean monadic code. To help alleviate this, one should instead
+    uses monads respecting this extended signature, that combines both the
+    Kleisli triple and categoric definitions and provides two let-binding
+    operators. The operator [let*] corresponds to the [bind] function
+    while the operator [let+] corresponds to the [map]. *)
+module type S = sig
+  type 'a t
+  val return : 'a -> 'a t
+  val flatten : 'a t t -> 'a t
+  val map  : ('a -> 'b  ) -> 'a t -> 'b t
+  val bind : ('a -> 'b t) -> 'a t -> 'b t
+  module Operators : sig
+    val ( >>-  ) : 'a t -> ('a -> 'b t) -> 'b t
+    val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
+    val ( >>-: ) : 'a t -> ('a -> 'b) -> 'b t
+    val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
+  end
+end
+
+
+
+(** {2 Extend minimal signature}
+
+    Those functors provide a way to extend a minimal signature into a full
+    monad that satisfies the signature defined above. This is possible
+    because one can defines operations from one monadic definition
+    using the operations required by the others. Indeed :
+
+    1. ∀m:('a t), ∀f:('a -> 'b), [map f m ≣ bind (fun x -> return (f x)) m]
+    2. ∀m:('a t t), [flatten m ≣ bind identity m]
+    3. ∀m:('a t), ∀f:('a -> 'b t), [bind f m ≣ flatten (map f m)]
+
+    All required laws expressed in both minimal signatures are respected
+    using those definitions. *)
+
+(** Extend a Kleisli triple monad *)
+module Extend_Kleisli (M : Kleisli) : S with type 'a t = 'a M.t
+
+(** Extend a categoric monad *)
+module Extend_Categoric (M : Categoric) : S with type 'a t = 'a M.t
+
+
+
+(** {2 Product on monads}
+
+    In a computational point of view, a monad is an abstraction of a sequence
+    of operations. But sometimes, one may need to specify that two operations
+    can be perform in *any* order, for instance when dealing with concurrency
+    or generic errors handling using the [option] type. To do so, one needs
+    to specify a *product* on monadic values, i.e a way to combine two monads
+    into a new one. The concept of a product has also deep roots in category
+    theory, but there is no need to dive into this for the purpose of this
+    module. *)
+module type Product = sig
+  type 'a t
+  val product : 'a t -> 'b t -> ('a * 'b) t
+end
+
+
+
+(** {3 Product on Kleisli triple monads}
+
+    When combined with a Kleisli triple monad, the product should respect
+    the following property if one wants it to truly encode that computations
+    can be performed in *any* order :
+
+    1. ∀l:('a t), ∀r:('b t), ∀f:(('a * 'b) -> 'c t),
+       [bind f (product l r) ≣ bind (fun (b, a) -> f (a, b)) (product r l)]
+
+    However, in some cases, this property is not feasible. In those cases, it
+    should be explicitly stated. *)
+module type Kleisli_with_product = sig
+  include Kleisli
+  include Product with type 'a t := 'a t
+end
+
+
+
+(** {3 Product on Categoric monads}
+
+    When combined with a Categoric monad, the product should respect the
+    following property if one wants it to truly encode that computations
+    can be performed in *any* order :
+
+    1. ∀l:('a t), ∀r:('b t), ∀f:(('a * 'b) -> 'c),
+       [map f (product l r) ≣ map (fun (b, a) -> f (a, b)) (product r l)]
+
+    However, in some cases, this property is not feasible. In those cases, it
+    should be explicitly stated.*)
+module type Categoric_with_product = sig
+  include Categoric
+  include Product with type 'a t := 'a t
+end
+
+
+
+(** {3 Extended monads with product}
+
+    Extended monads that provides a product also provides two and-binding
+    operators implementing it, the [and*] and [and+] operators.
+
+    Take note that a generic implementation of the product can be given
+    as [bind (fun a -> map (fun b -> (a, b)) r) l]. However, this
+    definition may not be symmetric, which is why one is required to
+    provide a custom product. *)
+module type S_with_product = sig
+  type 'a t
+  val return : 'a -> 'a t
+  val flatten : 'a t t -> 'a t
+  val map  : ('a -> 'b  ) -> 'a t -> 'b t
+  val bind : ('a -> 'b t) -> 'a t -> 'b t
+  val product : 'a t -> 'b t -> ('a * 'b) t
+  module Operators : sig
+    val ( >>-  ) : 'a t -> ('a -> 'b t) -> 'b t
+    val ( let* ) : 'a t -> ('a -> 'b t) -> 'b t
+    val ( and* ) : 'a t -> 'b t -> ('a * 'b) t
+    val ( >>-: ) : 'a t -> ('a -> 'b) -> 'b t
+    val ( let+ ) : 'a t -> ('a -> 'b) -> 'b t
+    val ( and+ ) : 'a t -> 'b t -> ('a * 'b) t
+  end
+end
+
+(** Extend a Kleisli triple monad with product *)
+module Extend_Kleisli_with_product (M : Kleisli_with_product) :
+  S_with_product with type 'a t = 'a M.t
+
+(** Extend a categoric monad *)
+module Extend_Categoric_with_product (M : Categoric_with_product) :
+  S_with_product with type 'a t = 'a M.t
diff --git a/src/libraries/utils/option.ml b/src/libraries/monads/option.ml
similarity index 86%
rename from src/libraries/utils/option.ml
rename to src/libraries/monads/option.ml
index dc7cf73b39f51f4cb9c2912f4ce5ce1acf34bada..beb92fc126b85fca0c40e97dbcce914714e933d7 100644
--- a/src/libraries/utils/option.ml
+++ b/src/libraries/monads/option.ml
@@ -22,11 +22,12 @@
 
 include Stdlib.Option
 
-let zip l r = match l, r with Some l, Some r -> Some (l, r) | _ -> None
-
-module Operators = struct
-  let ( let* ) m f = bind m f
-  let ( let+ ) m f = map f m
-  let ( and* ) l r = zip l r
-  let ( and+ ) l r = zip l r
+module Minimal = struct
+  type 'a t = 'a option
+  let return v = Some v
+  let bind f m = bind m f
+  let product l r = match l, r with Some l, Some r -> Some (l, r) | _ -> None
 end
+
+include Monad.Extend_Kleisli_with_product (Minimal)
+let bind m f = bind f m
diff --git a/src/libraries/utils/option.mli b/src/libraries/monads/option.mli
similarity index 78%
rename from src/libraries/utils/option.mli
rename to src/libraries/monads/option.mli
index 2f8cbed250f26dd3e4d3ae08c808f396c3d51642..f671ec61e15af5105bd69592bbdeabd39f8f0a21 100644
--- a/src/libraries/utils/option.mli
+++ b/src/libraries/monads/option.mli
@@ -20,16 +20,9 @@
 (*                                                                        *)
 (**************************************************************************)
 
-(* Adding let binding operators to the Option module. See
-   https://v2.ocaml.org/manual/bindingops.html for more information. *)
-
+(* Extend the [option] type to a full fleshed monad. *)
 include module type of Stdlib.Option
+include Monad.S_with_product with type 'a t = 'a option
 
-val zip : 'a option -> 'b option -> ('a * 'b) option
-
-module Operators : sig
-  val ( let* ) : 'a option -> ('a -> 'b option) -> 'b option
-  val ( let+ ) : 'a option -> ('a -> 'b) -> 'b option
-  val ( and* ) : 'a option -> 'b option -> ('a * 'b) option
-  val ( and+ ) : 'a option -> 'b option -> ('a * 'b) option
-end
+(* The bind is reversed for retrocompatibility reasons. *)
+val bind : 'a t -> ('a -> 'b t) -> 'b t
diff --git a/src/libraries/utils/result.ml b/src/libraries/monads/result.ml
similarity index 100%
rename from src/libraries/utils/result.ml
rename to src/libraries/monads/result.ml
diff --git a/src/libraries/utils/result.mli b/src/libraries/monads/result.mli
similarity index 100%
rename from src/libraries/utils/result.mli
rename to src/libraries/monads/result.mli
diff --git a/src/plugins/eva/api/values_request.ml b/src/plugins/eva/api/values_request.ml
index 07440aa5df998418c374ab4fd13264237c756b5f..a5bcb30055223b72a780bdb78f8195ac3772fe77 100644
--- a/src/plugins/eva/api/values_request.ml
+++ b/src/plugins/eva/api/values_request.ml
@@ -417,7 +417,7 @@ module Proxy(A : Analysis.S) : EvaProxy = struct
     | Status truth -> Alarmset.Status.pretty fmt truth
 
   let get_pointed_bases = function
-    | Value v -> get_bases (Bottom.fold ~bottom:Cvalue.V.bottom get_cvalue v.v)
+    | Value v -> get_bases Bottom.(map get_cvalue v.v |> value ~bottom:Cvalue.V.bottom)
     | Offsetmap offsm -> get_pointed_bases offsm
     | Status _ -> Base.Hptset.empty
 
@@ -445,7 +445,7 @@ module Proxy(A : Analysis.S) : EvaProxy = struct
     let pretty_eval = Bottom.pretty (pp_result typ) in
     let value = Pretty_utils.to_string pretty_eval result in
     let pointed_markers = get_pointed_markers eval_point in
-    let pointed_vars = Bottom.fold ~bottom:[] pointed_markers result in
+    let pointed_vars = Bottom.(map pointed_markers result |> value ~bottom:[]) in
     { value; alarms; pointed_vars }
 
   (* --- Evaluates an expression or lvalue into an evaluation [result]. ----- *)
diff --git a/src/plugins/eva/domains/octagons.ml b/src/plugins/eva/domains/octagons.ml
index 2d21392330438f3e357ebf4b8d659f4ba8e85c17..5e3ddcabfbca7b8a2b95528bb39e4a96fc0ad19e 100644
--- a/src/plugins/eva/domains/octagons.ml
+++ b/src/plugins/eva/domains/octagons.ml
@@ -1277,8 +1277,9 @@ module State = struct
   let mk_variable_builder (eval : evaluator) (_: t) =
     let (let*) x f = Option.bind (Top.to_option x) f in
     (* Is the interval computed for a variable a singleton? *)
-    let is_singleton =
-      Top.fold ~top:false Cvalue.V.cardinal_zero_or_one
+    let is_singleton v =
+      Top.map Cvalue.V.cardinal_zero_or_one v
+      |> Top.value ~top:false
     in
     fun exp ->
       match exp.node with
diff --git a/src/plugins/eva/engine/evaluation.ml b/src/plugins/eva/engine/evaluation.ml
index 06c1daa44ce5840a433e7793feb222aa697d56a2..e1e64a105b104b64e1fdc4d68cf94570dc8f64f7 100644
--- a/src/plugins/eva/engine/evaluation.ml
+++ b/src/plugins/eva/engine/evaluation.ml
@@ -1742,7 +1742,7 @@ module Make
           let cil_args = Option.map (List.map Eva_ast.to_cil_exp) args in
           let alarm = Alarms.Function_pointer (cil_v, cil_args) in
           let alarms = Alarmset.singleton ~status alarm in
-          Bottom.bot_of_list list, alarms
+          Bottom.return list, alarms
       end
     | _ -> assert false
 end
diff --git a/src/plugins/eva/engine/transfer_stmt.ml b/src/plugins/eva/engine/transfer_stmt.ml
index 839d17ea3a3b7461da610afb861ccae1477ac1d2..b768b4fa11a0665901f757d825845801aac7b15e 100644
--- a/src/plugins/eva/engine/transfer_stmt.ml
+++ b/src/plugins/eva/engine/transfer_stmt.ml
@@ -735,7 +735,7 @@ module Make (Engine: Engine_sig.S) = struct
               cacheable := NoCacheCallers;
             states
           in
-          Bottom.list_of_bot states
+          Bottom.value ~bottom:[] states
       in
       (* Process each possible function apart, and append the result list. *)
       let process acc (kf, valuation) =
@@ -743,7 +743,7 @@ module Make (Engine: Engine_sig.S) = struct
       in
       List.fold_left process [] functions
     in
-    Bottom.list_of_bot eval, !cacheable
+    Bottom.value ~bottom:[] eval, !cacheable
 
   (* ------------------------------------------------------------------------ *)
   (*                            Unspecified Sequence                          *)