let left z = match z.hole with
| Node (l,r) -> Some { hole = l; path = (Right,r) :: z.path }
| Leaf _ -> None
let right z = match z.hole with
| Node (l,r) -> Some { hole = r; path = (Left,l) :: z.path }
| Leaf _ -> None
let up z = match z.path with
| [] -> None
| (Left, l) :: path ->
Some { path; hole = Node(l,z.hole) }
| (Right, r) :: path ->
Some { path; hole = Node(z.hole,r) }
type zipper = { hole: hole; path: step list }
and hole =
| Name of name
| Module_type of module_type
| Module of module_
| Module_expr of module_ expr
| Module_type_expr of module_type expr
type step =
| Module_ident
| Module_type_ident
| Expr_open
| Bind_str of string
| Bind_type_str of name
| Bind_sig of string
| Bind_type_sig of name
| Str of {left:module_ expr list; right:module_ expr list}
| Sign of {left:module_type expr list; right:module_type expr list}
| Constraint_left of module_type
| Constraint_right of module_
exception Stop
let up z = match z.hole, z.path with
| Name name, Module_ident :: path -> { hole = Module (Ident name); path }
| Name name, Module_type_ident :: path -> { hole = Module_type (Ident name); path }
| Module me, Expr_open :: path -> { hole = Module_expr (Open me); path }
| Module_type mt, Expr_open :: path -> { hole = Module_type_expr (Open mt); path }
| Module me, Constraint_left mt :: path -> { hole = Module (Constraint(me,mt)); path }
| Module_type mt, Constraint_right me :: path -> { hole = Module(Constraint(me, mt)); path }
| Module_expr e, Str {left; right } :: path ->
{ hole = Module(Structure (List.rev_append left (e :: right))); path }
| Module_type_expr mte, Sign {left; right } :: path ->
{ hole = Module_type(Signature (List.rev_append left (mte :: right))); path }
| Module m, Bind_str name :: path ->
{ hole = Module_expr(Bind(name,m)); path }
| Module_type mt, Bind_type_str name :: path ->
{ hole = Module_expr(Bind_type(name,mt)); path }
| Module_type mt, Bind_sig name :: path ->
{ hole = Module_type_expr(Bind(name,mt)); path }
| Module_type mt, Bind_type_sig name :: path ->
{ hole = Module_type_expr(Bind_type(name,mt)); path }
| Module m, [] -> raise Stop
| _ -> assert false
let nonsense = { hole = Structure []; path = [Module_ident] }
Exhaustiveness checking is impossible
Lot of redundancy
Two class of types
- a zipper type
- a familly of path type for each final type of the path
- A foreign constructor
Root
type zipper =
| String_hole of string * to_string
| Module_expr_hole of module_ expr * to_module_expr
| Module_type_expr_hole of module_type expr * to_module_type_expr
| Module_hole of module_ * to_module
| Module_type_hole of module_type * to_module_type
and to_string =
| Module_Ident of to_module
| Module_type_ident of to_module_type
and to_module_type =
| Expr_open of to_module_type_expr
| Constraint of {data: module_; prev: to_module}
| Bind of {data: string; prev:to_module_expr }
| Bind_sig of {data: string; prev:to_module_type_expr }
and to_module_expr =
| Structure of {left: module_ expr list; prev: to_module; right: module_ expr list}
and to_module_type_expr =
| Signature of {left: module_type expr list; prev: to_module_type; right: module_type expr list}
and to_module =
| Root
| Expr_open of to_module_expr
| Constraint of {data:module_type; prev:to_module}
| Bind of {data: string; prev:to_module_expr }
| Bind_sig of {data: string; prev:to_module_type_expr }
exception Stop
let up = function
| String_hole (s, prev) ->
begin match prev with
| Module_Ident prev -> Module_hole(Ident s, prev)
| Module_type_ident prev -> Module_type_hole(Ident s, prev)
end
| Module_expr_hole (m, Structure {left; right; prev}) ->
Module_hole (Structure (List.rev_append left (m::right)), prev)
| Module_type_expr_hole (m, Signature {left; right; prev}) ->
Module_type_hole (Signature (List.rev_append left (m::right)), prev)
| Module_hole (m, prev) ->
begin match prev with
| Root -> raise Stop
| Expr_open prev -> Module_expr_hole(Open m, prev)
| Constraint {data; prev} -> Module_hole(Constraint(m,data), prev)
| Bind {data; prev} -> Module_expr_hole(Bind(data,m),prev)
| Bind_sig {data; prev} -> Module_type_expr_hole(Bind(data,m),prev)
end
| Module_type_hole(mt,prev) ->
begin match prev with
| Expr_open prev -> Module_type_expr_hole(Open mt, prev)
| Constraint {data; prev} -> Module_hole(Constraint(data,mt), prev)
| Bind {data; prev} -> Module_expr_hole(Bind_type(data,mt),prev)
| Bind_sig {data; prev} -> Module_type_expr_hole(Bind_type(data,mt),prev)
end
- Still some redundancy
- Nested matches
- The list of element is hidden
Can we get back to a tree zipper as a list of unvisited subtrees ?
type ('a,'b) zipper = { hole:'a; path:('a,'b) path }
and ('a,'b) path =
| []: ('a,'a) path
| (::): ('a,'b) step * ('b,'c) path -> ('a,'c) path
and (_,_) step =
| Module_Ident: (string, module_) step
| Module_type_ident: (string, module_type) step
| Expr_open: ('a, 'a expr) step
| Constraint_left: module_type -> (module_,module_) step
| Constraint_right: module_ -> (module_type, module_) step
| Str: { left: module_ expr list; right:module_ expr list } ->
(module_ expr, module_) step
| Sign: { left: module_type expr list; right:module_type expr list } ->
(module_type expr, module_type) step
| Bind_str:string -> (module_, module_ expr) step
| Bind_sig:string -> (module_, module_type expr) step
| Bind_type_str:string -> (module_type, module_ expr) step
| Bind_type_sig:string -> (module_type, module_type expr) step
type 'b any = Any: ('a,'b) zipper -> 'b any
Only valid paths can be constructed
let valid = [ Module_ident; Bind_str "M"; Str {left=[];right=[]} ]
let invalid = [ Module_ident; Module_ident ]
^^^^^^^^^^^^
Error: This expression has type (string, module_) step
but an expression was expected of type (module_, 'a) step
Type string is not compatible with type module_
Only valid zippers too
let valid = { hole = "A"; path = [Module_ident]
let invalid = { hole = "A"; path = [Bind_str "B"] }
^^^^^^^^^^^^
Error: This expression has type (module_, module_ expr) step
but an expression was expected of type (string, 'a) step
Type module_ is not compatible with type string
let up (type b) (Any z: b any): b any = match z.path with
| Module_Ident :: path -> Any { hole = Ident z.hole; path }
| Module_type_ident :: path -> Any { hole = (Ident z.hole:module_type); path }
| Expr_open :: path -> Any { hole = Open z.hole; path }
| Constraint_left mt :: path -> Any { hole = Constraint(z.hole,mt); path }
| Constraint_right me :: path -> Any { hole = Constraint(me, z.hole); path }
| Str {left; right } :: path ->
Any { hole = Structure (List.rev_append left (z.hole :: right)); path }
| Sign {left; right } :: path ->
Any { hole = Signature (List.rev_append left (z.hole :: right)); path }
| Bind_str name :: path ->
Any { hole = Bind(name,z.hole); path }
| Bind_type_str name :: path ->
Any { hole = Bind_type(name,z.hole); path }
| Bind_sig name :: path ->
Any { hole = Bind(name,z.hole); path }
| Bind_type_sig name :: path ->
Any { hole = Bind_type(name,z.hole); path }
| [] -> raise Stop
-
Fully typed
-
No redundancies
-
Unnecessary types can be erased with existentials
- Ast definition: 80 lines
- Zipper definition: 120-240 lines
- Fold: 500 lines
- Generic printer: 200 lines
- Zipper are an interesting structure for decomposing computation
- They may become unwieldy for complex heterogeneous trees
-
Combining them with GADTs to avoid:
- invalid states
- redundancy