This is harder than it ought to be. To declare mutually recursive record types is easy:
type shop = {
name: string;
sell: trinket -> unit }
and trinket = {
price: int;
purchase: shop -> unit }
Mutually recursive class types are also easy:
class type shop = object
method name: string
method sell: trinket -> unit
end
and trinket = object
method purchase: shop -> unit
end
But because the syntax for declaring class types is distinct from the syntax for declaring all other types, to make a record and a class mutually recursive is a little trickier. You can either have the recursion happen in the record:
class type ['a] trinket = object
method price: int
method purchase: 'a -> unit
end
type shop = {
name: string;
sell: shop trinket -> unit }
class trinket (price: int) = object (self)
method price = price
method purchase location = location.sell (self :> trinket)
end
or in the class:
type 'a shop = {
name: string;
sell: 'a -> unit }
class trinket (price: int) = object (self)
method price = price
method purchase location = location.sell (self :> trinket)
end
One other way to do it is to declare the class type with the non-class type syntax:
type shop = {
name: string;
sell: trinket -> unit }
and trinket = <
price: int;
purchase: shop -> unit >
However with this method, trinket can't inherit from another class. You could add in every superclass's methods manually... If you could inherit with this syntax I think it would be superior.
Doing it in the class is simplest, except you're left with a parameterized record type. I'd rather have the parameterized class type, since I'm not going to actually use it for anything besides declaring the record type. I suspect most people deal with this issue by just making everything a class rather than a record.
The weird thing is that normally when you declare a class, you're declaring both a type and specifying the executable code that is used with the type. The type system in OCaml starts to seem annoying when you mix it with classes. I'm actually having a hard time understanding why the (self :> trinket) is necessary. Shouldn't the type inferer figure that out on its own?