Dans notre cas, on choisi plutôt d'ajouter un `location`.
type sexp = Sexp of location * sexp' sexp_f
Le ' est en trop, non?
Et on peut définir nos schémas de récursion classiques.
let map (eval : 'a -> 'b) : 'a f -> 'b f = function | String (location, value) -> String (location, value) | Integer (location, value) -> Integer (location, value) | Node (location, head, tail) -> Node (location, eval head, List.map eval tail)
Hmm... je m'attendais à:
let map (eval : 'a -> 'b) : 'a sexp_f -> 'b sexp_f = function | String (value) -> String (value) | Integer (value) -> Integer (value) | Node (head, tail) -> Node (eval head, List.map eval tail)
Le problème est que ces schémas ne sont pas vraiment utiles parce que le location d'un nœud se situe « en haut » de ce nœud et n'est donc pas directement accessible. On peut définir un autre schéma qui ajoute la `location`.
Pas sûr quand on utiliserait on catamorphisme (ou autres *morphisme), mais pour la "recursion explicite" habituelle, ça ressemble à:
fun foo s = match s with | Sexp (loc, Symbol value) -> ... | Sexp (loc, Integer value) -> ... | Sexp (loc, Node (head, tail)) -> ...
ou
fun foo s = let (loc, s') = s in match s with | Symbol value -> ... | Integer value -> ... | Node (head, tail) -> ...
type 'carrier sexp_f = | String of location * string | Integer of location * integer | Node of location * 'carrier * 'carrier list
Je crois qu'on aura pas souvent l'occasion d'utiliser la flexibilité offerte ici par `carrier` :-(
Par contre, c'est effectivement plus efficace en terme d'usage mémoire.
Ma suggestion: conserver les `location` dans chaque constructeur de valeur.
Et ça fera beaucoup moins de changements :-)
Stefan