Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature wish: better pretty-printing for cyclic values #3421

Closed
vicuna opened this issue Jul 10, 2002 · 3 comments
Closed

Feature wish: better pretty-printing for cyclic values #3421

vicuna opened this issue Jul 10, 2002 · 3 comments
Assignees

Comments

@vicuna
Copy link

vicuna commented Jul 10, 2002

Original bug ID: 1231
Reporter: administrator
Assigned to: @mshinwell
Status: closed (set by @xavierleroy on 2015-12-11T18:27:44Z)
Resolution: fixed
Priority: normal
Severity: feature
Fixed in version: 4.02.0+beta1 / +rc1
Category: ~DO NOT USE (was: OCaml general)
Has duplicate: #6228
Monitored by: jm

Bug description

Hello,

l'affichage d'une valeur circulaire (créée avec un let rec ou avec
des effets de bords) dans le toplevel n'est pas très satisfaisante:
il ne termine que parce que le pretty-printer s'arrête à une certaine
profondeur.

On peut imaginer de detecter les boucles comme pour les types récursifs
et de les briser en introduisant des alias, par exemple:

type t = { x : int; mutable y : t option };;
let a = { x = 10; y = None };;
a.y <- Some a;;
a;;

donnerait:

{ x = 10; y = Some #a } as #a

ou peut-être:

#a where #a = { x = 10; y = Some #a}

(ou n'importe quelle autre syntaxe pour les «variables de valeurs»).

La deuxième possibilité permet parfois d'y voir plus clair
lorsqu'il y a des boucles imbriquées, si on autorise la forme
plus générale: v where #a1 = v1 and ... and #an = vn.
Par exemple, pour let rec x = 1 :: y and y = 2 :: x in (x,y) :

(#a, #b) where #a = 1 :: #b and #b = 2 :: #a

(pour les listes, il faudrait renoncer aux crochets, et utiliser
explicitement le constructeur (::))

Un affichage soigné des valeurs cycliques serait très appréciable lorsque
l'on fait dans le toplevel la mise au point d'algorithmes sur des graphes.
Je ne vois pas vraiment de difficultés techniques. Est-ce que vous
envisagez d'implémenter ce genre de chose ?

-- Alain

@vicuna
Copy link
Author

vicuna commented Jul 10, 2002

Comment author: administrator

On peut imaginer de detecter les boucles comme pour les types récursifs
et de les briser en introduisant des alias, par exemple:
[...]
type t = { x : int; mutable y : t option };;
let a = { x = 10; y = None };;
a.y <- Some a;;
a;;

donnerait:

{ x = 10; y = Some #a } as #a

ou peut-être:

#a where #a = { x = 10; y = Some #a}

(ou n'importe quelle autre syntaxe pour les «variables de valeurs»).

La deuxième possibilité permet parfois d'y voir plus clair
lorsqu'il y a des boucles imbriquées, si on autorise la forme
plus générale: v where #a1 = v1 and ... and #an = vn.
Par exemple, pour let rec x = 1 :: y and y = 2 :: x in (x,y) :

(#a, #b) where #a = 1 :: #b and #b = 2 :: #a

(pour les listes, il faudrait renoncer aux crochets, et utiliser
explicitement le constructeur (::))

Un affichage soigné des valeurs cycliques serait très appréciable lorsque
l'on fait dans le toplevel la mise au point d'algorithmes sur des graphes.
Je ne vois pas vraiment de difficultés techniques. Est-ce que vous
envisagez d'implémenter ce genre de chose ?

Le coût de détection des cycles n'est évidemment pas nul, même s'il
est supportable en pratique.

D'habitude (dans les anciennes implémentations de Caml qui avaient ce
trait), on écrivait

t0 where t0 = ... and t1 = ...

où t était le nom du type concerné. On pourrait maintenant plutôt
écrire

let rec t0 = ... and t1 = ... in
t0

Qui aurait aussi l'avantage d'être un programme Caml légal...

Pierre Weis

INRIA, Projet Cristal, Pierre.Weis@inria.fr, http://pauillac.inria.fr/~weis/

@vicuna
Copy link
Author

vicuna commented Jul 10, 2002

Comment author: administrator

On Wed, 10 Jul 2002, Pierre Weis wrote:

Le coût de détection des cycles n'est évidemment pas nul, même s'il
est supportable en pratique.

Oui ! Mais de toute manière, il y a une limite à la taille de la
valeur affichée, donc le surcout est une constante.

D'habitude (dans les anciennes implémentations de Caml qui avaient ce
trait), on écrivait

t0 where t0 = ... and t1 = ...

Ah tiens, pourquoi ça a changé ? J'ai justement choisi cette notation
pour mon language CDuce; les types récursifs me semblent beaucoup plus
lisibles ainsi.

où t était le nom du type concerné. On pourrait maintenant plutôt
écrire

let rec t0 = ... and t1 = ... in
t0

Qui aurait aussi l'avantage d'être un programme Caml légal...

Comme une demande a toujours plus de poids quand on envoie du code avec,
voici un patch par rapport à la version CVS:

Index: toplevel/genprintval.ml

RCS file: /caml/ocaml/toplevel/genprintval.ml,v
retrieving revision 1.31
diff -r1.31 genprintval.ml
176c176,199
< let rec tree_of_val depth obj ty =

  let counter = ref 0 in
  let named = ref [] in
  let new_name result =
    incr counter;
    let name = "v" ^ string_of_int !counter in
    named := (name,result) :: !named;
    name in

  let rec tree_of_val visited depth obj ty =

try
let (alias,result) = List.assq obj visited in
let name = match !alias with
| Some x -> x
| None ->
let x = new_name result in
alias := Some x;
x
in
Oval_stuff name
with Not_found ->
begin
let myname = ref None in
let mytree = ref None in
let visited = (obj, (myname,mytree)) :: visited in
182a206
let result =
189c213
< Oval_tuple (tree_of_val_list 0 depth obj ty_list)


          Oval_tuple (tree_of_val_list 0 visited depth obj ty_list)

191c215
< tree_of_exception depth obj

          tree_of_exception visited depth obj

203c227
< tree_of_val (depth - 1) (O.field obj 0) ty_arg in

                      tree_of_val visited (depth - 1) (O.field obj 0) ty_arg in

223c247
< tree_of_val (depth - 1) (O.field obj i) ty_arg in

                      tree_of_val visited (depth - 1) (O.field obj i) ty_arg in

233c257
< then let v = tree_of_val depth (Lazy.force (O.obj obj)) ty_arg in

          then let v = tree_of_val visited depth (Lazy.force (O.obj obj)) ty_arg in

243c267
< tree_of_val depth obj

                tree_of_val visited depth obj

260c284
< constr_name 0 depth obj ty_args

                                       constr_name 0 visited depth obj ty_args

276c300
< tree_of_val (depth - 1) (O.field obj pos)

                            tree_of_val visited (depth - 1) (O.field obj pos)

298c322
< tree_of_val (depth - 1) (O.field obj 1) ty in

                          tree_of_val visited (depth - 1) (O.field obj 1) ty in

316c340
< tree_of_val (depth - 1) obj ty

          tree_of_val visited (depth - 1) obj ty

320c344
< tree_of_val (depth - 1) obj ty

          tree_of_val visited (depth - 1) obj ty

322a347,350

in
match !myname with
  | Some x -> mytree := Some result; Oval_stuff x
  | None -> result

323a352

end
325c354
< and tree_of_val_list start depth obj ty_list =


  and tree_of_val_list start visited depth obj ty_list =

329c358
< let tree = tree_of_val (depth - 1) (O.field obj i) ty in

          let tree = tree_of_val visited (depth - 1) (O.field obj i) ty in

334c363
< tree_of_cstr cstr_name start depth obj ty_args =

         tree_of_cstr cstr_name start visited depth obj ty_args =

336c365
< let args = tree_of_val_list start depth obj ty_args in

    let args = tree_of_val_list start visited depth obj ty_args in

339c368
< and tree_of_exception depth bucket =

and tree_of_exception visited depth bucket =

355c384
< (fun x -> Oide_ident x) name 1 depth bucket cstr.cstr_args

       (fun x -> Oide_ident x) name 1 visited depth bucket cstr.cstr_args

361c390,398
< in tree_of_val max_depth obj ty

in
  let result = tree_of_val [] max_depth obj ty in
  let named =

List.rev_map
(function (x,{contents=Some y}) -> (x,y)
| _ -> assert false)
!named in
Oval_aliases (result, named)

Index: typing/oprint.ml

RCS file: /caml/ocaml/typing/oprint.ml,v
retrieving revision 1.7
diff -r1.7 oprint.ml
52a53,57

| Oval_aliases (v, b) ->

if b = []
then print_simple_tree ppf v
else fprintf ppf "@[<1>let rec@ %a@ in@ %a@]" print_aliases b
print_tree_1 v
53a59,65
and print_aliases ppf = function
| [ (name,alias) ] ->
fprintf ppf "@[<1>%s@ =@ %a@]" name print_tree_1 alias
| (name,alias) :: r ->
fprintf ppf "@[<1>%s@ =@ %a@ and@ %a@]" name print_tree_1
alias print_aliases r
| [] -> assert false
Index: typing/outcometree.mli
===================================================================
RCS file: /caml/ocaml/typing/outcometree.mli,v
retrieving revision 1.7
diff -r1.7 outcometree.mli
42a43
| Oval_aliases of out_value * (string * out_value) list

Notes:

  • les alias sont tous introduits au niveau supérieur
  • les chaines de formattage pourraient être mieux écrites
  • pas de traitement particulier pour les listes (il en faudrait)
  • ça ne met en évidence que les égalités physiques entre une valeur
    et celles qui sont accessibles à partir de celle-ci, donc:

let rec x = (x,y) and y = (y,x) in (x,y);;

  • : ('a * ('b * 'a as 'b) as 'a) * ('c * ('d * 'c as 'd) as 'c) =
    let rec v1 = (v1, v2) and v2 = (v2, v1) and v3 = (v3, v4) and v4 = (v4, v3)
    in (v1, v3)

    Pour voir toutes les égalités (est-ce souhaitable ?), il faut
    changer un peu plus la structure du code de Genprintval

-- Alain

@vicuna
Copy link
Author

vicuna commented May 30, 2014

Comment author: @mshinwell

I think the fix to 6228 resolves this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants