mirage / repr

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support mutually-recursive groups of size greater than 2

craigfe opened this issue · comments

We currently have mu2 to support pairs of mutually recursive types, but no real way to handle the general case. A simple solution might be to just add combinators for a few more group sizes:

val mu3 : ('a t -> 'b t -> 'c t -> ('a t * 'b t * 'c t as 'res)) -> 'res
val mu4 : ('a t -> 'b t -> 'c t -> 'd t -> ('a t * 'b t * 'c t * 'd t as 'res)) -> 'res
(* etc. *)

... or perhaps there is a generic solution via Oleg.

Actually from my experiments mu2 supports more than pairs of mutually recursive types, it supports all possible recursive types as long as you can build them all from 2 of them.

Example:

type a2 = { fa1 : b2 option; fa2 : c2 option }
and b2 = { fb1 : c2 option; fb2 : d2 option }
and c2 = { fc1 : a2 option; fc2 : d2 option }
and d2 = { fd1 : a2 option; fd2 : b2 option; fd3 : c2 option }

open Irmin.Type

let mka2_t b2_t c2_t =
  record "a2" (fun fa1 fa2 -> { fa1; fa2 })
  |+ field "fa1" (option b2_t) (fun t -> t.fa1)
  |+ field "fa2" (option c2_t) (fun t -> t.fa2)
  |> sealr

let mkb2_t c2_t d2_t =
  record "b2" (fun fb1 fb2 -> { fb1; fb2 })
  |+ field "fb1" (option c2_t) (fun t -> t.fb1)
  |+ field "fb2" (option d2_t) (fun t -> t.fb2)
  |> sealr

let mkc2_t a2_t d2_t =
  record "c2" (fun fc1 fc2 -> { fc1; fc2 })
  |+ field "fc1" (option a2_t) (fun t -> t.fc1)
  |+ field "fc2" (option d2_t) (fun t -> t.fc2)
  |> sealr

let mkd2_t a2_t b2_t c2_t =
  record "d2" (fun fd1 fd2 fd3 -> { fd1; fd2; fd3 })
  |+ field "fd1" (option a2_t) (fun t -> t.fd1)
  |+ field "fd2" (option b2_t) (fun t -> t.fd2)
  |+ field "fd3" (option c2_t) (fun t -> t.fd3)
  |> sealr

let b2_t, c2_t =
  mu2 (fun b2 c2 ->
      let a2 = mka2_t b2 c2 in
      let d2 = mkd2_t a2 b2 c2 in
      let c2 = mkc2_t a2 d2 in
      let b2 = mkb2_t c2 d2 in
      (b2, c2))

let a2_t = mka2_t b2_t c2_t

let d2_t = mkd2_t a2_t b2_t c2_t

let () =
  let a = { fa1 = None; fa2 = None } in
  let b = { fb1 = None; fb2 = None } in
  let c = { fc1 = None; fc2 = None } in
  let d = { fd1 = Some a; fd2 = Some b; fd3 = Some c } in
  Format.eprintf "%a@." Repr.(pp d2_t) d

Results in

{"fd1":{},"fd2":{},"fd3":{}}

Maybe we could improve the doc by showing that this is a possibility actually

Indeed, the actual limitations of mu2 are somewhat hard to capture (which is why there's been a TODO in its documentation for a long time). FWIW, I think the actual constraint on groups is something like "the directed graph of type co-dependencies can't contain any complete subgraphs of size greater than 2.".

I agree with the formalised constraint :-)