[Solved] Declaring Variant type of records with duplicate field names


#1

Hi -

So it looks like OCaml/ReasonML won’t let your define records with duplicate field names. A workaround seems to be defining each record as part of a different module.

How do I then define a variant type with these records?

module Log_entry = {
  type t = {
    session_id: string,
    important: bool,
    message: string,
  };
};
module Heartbeat = {
  type t = {
    session_id: string,
    status_message: string,
  };
};
module Logon = {
  type t = {
    session_id: string,
    user: string,
    credentials: string,
  };
};

type x = 
| Logon.t 
| Heartbeat.t
| Logon.t;

type x complains that statements must end with a semicolon.

Appreciate any response.
Thanks
Kesava


#2

Try an actual variant :wink:

type x = 
| LogEntry(LogEntry.t) 
| Heartbeat(Heartbeat.t)
| Logon(Logon.t);

What you wrote wouldn’t even fly in TypeScript (you’d need a common string literal field for a discriminated union).

On the bright side, a switch over a Reason union type is more concise than that over a TS discriminated union.


#3

Thank you! That should have been obvious. :slight_smile:


#4

Just to clarify, this is not a workaround, this is the intention; field names in OCaml are deliberately scoped to modules.


#5

ok, I get it. So, how does one define recursive types across these modules? I feel like the complexity has gone up suddenly :frowning:


#6

Let me be a little more explicit about, what I mean. I am trying to model handlebars AST in reasonML types.

So I have a bunch of nodes defined like the following -

module StringLiteral = {
  type t = {
    original: original,
    value: value,
    loc: loc
  };
};

module BooleanLiteral = {
  type t = {
    original: bool,
    value: bool,
    loc: loc
  }
};

module PathExpression = {
  type t = {
    data: bool,
    depth: int,
    parts: list(string),
    original: string,
    loc: loc
  };
};

Defining a variant type using the above types seems pretty trivial (thanks to @hoichi’s response). I just define the following.

type paramsExpr =
| PathExpression(PathExpression.t)
| BooleanLiteral(BooleanLiteral.t)
| StringLiteral(StringLiteral.t);

However, things seem pretty complex, when declaring recursive types across these modules.

module BlockStatement = {
  type t = {
    path: PathExpression.t,
    params: list(paramsExpr),
    program: Program.t,
    inverse: Program.t,
    loc: loc
  }
};

type bodyExpr = 
| PathExpression(PathExpression.t)
| ContentStatement(ContentStatement.t)
| MustacheStatement(MustacheStatement.t)
| BlockStatement(BlockStatement.t);

module Program = {
  type t = {
    body: list(bodyExpr),
    loc: loc
  };
};

All three types in this above selection should be mutually recursive. How do I use and across module boundaries to define a recursive type?

I feel stuck, any help appreciated.

Thanks
Kesava


#7

Looks like this works. (Thanks to @yawaramin’s reply on discord app)

module rec HandlebarsAST: {
  module BlockStatement: {
    type t = {
      path: PathExpression.t,
      params: list(paramsExpr),
      program: HandlebarsAST.Program.t,
      inverse: HandlebarsAST.Program.t,
      loc: loc
    }
  };

  type bodyExpr = 
  | PathExpression(PathExpression.t)
  | ContentStatement(ContentStatement.t)
  | MustacheStatement(MustacheStatement.t)
  | BlockStatement(HandlebarsAST.BlockStatement.t);

  module Program: {
    type t = {
      body: list(HandlebarsAST.bodyExpr),
      loc: loc
    };
  };
} = HandlebarsAST;