Suggestions for representing a bunch of connected types?

I am implementing an API client, and the API is very connected, so if you have two types, responses, A and B, A contains a method for accessing its associated, B, and B contains a function to access its associated A, and there are numerous other types involved which have a similar relationship. A and B also have many fields in common, such as name and date or whatever.

At first I thought a bunch of mutually recursive records was an option but I’m not sure what to do about accessing all the overlapping fields. Recursive modules would be nice as they allow namespacing each type, but the amount of typing involved in recursive modules feels a lot.

The way I’m trying to represent things is the responses are records and they do not contain an instance of each other, but the URL to query the other then I have functions to take the input and return the other. I would like the methods namespaced with the type, though, otherwise I could do something like below:

module Type:
    module A : sig
        type t
    end
    module B : sig
        type t
    end
end

module Op : sig
    module A : sig
        val get_b : Type.A.t -> Type.B.t
    end

   module B : sig
         val get_a : Type.B.t -> Type.A.t
    end
end

Any other suggestions for the best way to approach this?

Is this an interface to an in-memory module, or an RPC interface? [your wording suggests the latter, but just want to check]

Also, do the A and B have different identities in the backing implementation / server? Or are they really aspects of the same (uh) “object” ?

Have you considered using objects ? You could have a class type for the common fields, and then two mutually-recursive classes that extend it with the specific fields.

Using records, one approach is to use composition (in the OO sense) instead of extension:

type 'a common = {
  <common fields>;
  specific: 'a;
}
type specific_a = { ...; b : b }
and specific_b = { ...; a : a }
and a = specific_a common
and b = specific_b common

It’s an RPC interface. There is no heirarchy, it’s just the contents of the JSON.

I am generally uninclined to use objects, maybe irrationally at this point. The record composition solution I think becomes unmanageable pretty quickly because the venn diagram of overlapping across the hundred or so of responses is pretty ridiculous.

What I have been playing with now is I have a module per record, and then modules for operations on the type. Something like:

module Type : sig
  module Org : sig
    type t
  end

  module Repo : sig
    type t
  end

  module Issue : sig
    type t
  end
end

module Response : sig
  type 'a t

  val value : 'a t -> 'a

  module Org : sig
    type t = Type.Org.t

    val issues_url : t -> Type.Issue.t list Request.t

    val repos_url : t -> Type.Repo.t list Request.t

    val url : t -> Uri.t
  end
end

It looks a bit awkward but it’s not so bad and really users of the client I’m writing will never interact with the Type’s module.

What do you think? If you stumbled upon this API would be be like “what was this guy doing at the time???”