Do I need polymorphic variants?

Using many features (objects, polymorphic variants, recursive types, GADT) and some known tricks I ended up with a way to encode deterministic finite state machine with heterogenously typed states (so that only allowed transitions are accepted by the type checker), but it’s a bit heavy (just an example for 3 states, 3 operations and 7 allowed transitions):

type state0 =
  < trans0 : state1;
    trans1 : state2;
    trans2 : [`forbidden];
    id     : [`_0] >
and state1 =
  < trans0 : state2;
    trans1 : state0;
    trans2 : state2;
    id     : [`_1] >
and state2 =
  < trans0 : state0;
    trans1 : [`forbidden];
    trans2 : state2;
    id     : [`_2] >

type data0 =
  {
    field0 : int;
    field1 : string
  }

type data1 = A | B of int

type data2 = int list option

type ('state, 'data) estate =
  | State0 : (state0, data0) estate
  | State1 : (state1, data1) estate
  | State2 : (state2, data2) estate

type 'state fsm_state = Ex : ('state, 'data) estate * 'data -> 'state fsm_state

let trans0 : type s0 s1 s2 id. < trans0 : s0; trans1 : s1; trans2 : s2; id : id > fsm_state -> s0 fsm_state =
  function
  | Ex (State0, { field0; field1 }) -> Ex (State1, if field0 < 0 && field1 = "" then A else B field0)
  | Ex (State1, A)                  -> Ex (State2, None)
  | Ex (State1 ,B i)                -> Ex (State2, Some [i])
  | Ex (State2, Some (x :: xs))     -> Ex (State0, { field0 = x; field1 = "" })
  | Ex (State2, (None | Some []))   -> Ex (State0, { field0 = 0; field1 = "" })

let trans1 :
  type s0 s10 s11 s12 s1i s2 id.
  < trans0 : s0; 
    trans1 : < trans0 : s10; trans1 : s11; trans2 : s12; id : s1i > as 'u;
    trans2 : s2;
    id : id > fsm_state -> 'u fsm_state =
  function
  | Ex (State0, { field0; field1 }) -> Ex (State2, if field0 < 0 && field1 = "" then Some [field0] else None)
  | Ex (State1, A)                  -> Ex (State0, { field0 = 0; field1 = "" })
  | Ex (State1 ,B i)                -> Ex (State0, { field0 = i; field1 = string_of_int i})

let rec trans2 :
  type s0 s1 s20 s21 s22 s2i id.
  < trans0 : s0;
    trans1 : s1;
    trans2 : < trans0 : s20; trans1 : s21; trans2 : s22; id : s2i > as 'u;
    id : id > fsm_state -> 'u fsm_state =
  function
  | Ex (State1, A)                  -> trans2 @@ Ex (State1, B 0)
  | Ex (State1, B i)                -> Ex (State2, None)
  | Ex (State2, None)               -> Ex (State2, Some [])
  | Ex (State2, Some [])            -> Ex (State2, None)
  | Ex (State2, Some (x :: xs))     -> trans2 @@ Ex (State2, Some xs)

let r0 = Ex (State0, { field0 = 1; field1 = ""}) |> trans1 |> trans0
let r1 = Ex (State0, { field0 = 1; field1 = ""}) |> trans1 |> trans0 |> trans0
let r2 = Ex (State0, { field0 = 1; field1 = ""}) |> trans1 |> trans0 |> trans0 |> trans2

(* let re = Ex (State0, { field0 = 1; field1 = ""}) |> trans1 |> trans1 *) (* Type error *)