Search code examples
typesinterfaceocamlsignaturesubtype

Subtype constraints in interfaces


I want to compose several "traits" across several modules. A function might require multiple such "traits" as its input, i.e.:

type 'a x_t = < get_x : int ; .. > as 'a constraint 'a = < get_b : float ; .. >
val foo : x_t -> float 

composing these traits manually in the interface is cumbersome and error-prone, but perfectly possible.

But in an ideal world, I should be able to use the "trait's" name instead of manually composing all the required fields, i.e. write something like:

 type 'a x_t = < get_x : int ; .. > as 'a constraint 'a <: a_t

Unfortunately, the OCaml syntax does not allow for this. This seems to be a purely syntactical restriction, so I wonder whether there is a deeper reason for this?

In other words: Why can't I directly write subtype constraints in OCaml type signatures?

edit: To clarify the use-case: I have several (sometimes interdependent) modules which provide functionality on some common shared state. These modules shall be composable and loosely coupled (i.e., the only require their part of the global state to fulfill their needs and thus, I can always extend the state with new functionality). To achive this, the state is encapsulated in an object which provides several lenses (above I only used the getters, not the setters). Hence, if I provide interface definitions for my modules I need to describe the required lenses in the signature of my functions via constraints on the type parameter that encodes the whole state type. Now I am searching for a way to write these composed signature requirements as short and readable as possible.


Solution

  • If you define your traits as class types instead of object types, you can use the #foo syntax.

    class type b = object
      method get_b : float
    end
    
    class type c = object
      method get_c : bool
    end
    
    type 'a x_t = < .. > as 'a 
      constraint 'a = #b
      constraint 'a = #c
    

    You can also compose traits through the inheritance mechanism:

    class type e = object
      inherit b inherit c
    end
    

    (Looks like types that are too clever for their own good, but why not experiment...)