Row parametric polymorphism can be quite similar to subtyping. However, the presence of GADT magnifies the difference between the two, because GADTs equations will often freeze the row type variable at the most inopportune time, and break the expected subtyping relationships.

Consider for instance, the toy language:

```
type _ expr =
| Int: int -> <bool_like:yes; num:yes; value:yes; ty:int > expr
| Float:
float ->
< bool_like:no; num:yes; value:yes; ty:float > expr
| If:
<bool_like:yes; num:_; value:_; ty:_ > expr
* (<bool_like:'b; num:yes; value:_;ty:'ty> as 'e) expr
* 'e expr
-> <bool_like:'b; num:yes; value:no; ty:'ty > expr
| Add:
(<num:yes; bool_like:'b; value:_; ty:'ty> as 'e) expr
* 'e expr
-> <num:yes; bool_like:'b; value:no; ty:'ty> expr
```

One can write an interpreter to the syntactic values of this language with

```
let rec value: type b n v ty.
< bool_like:b; num:n; value:v; ty:ty> expr
-> <bool_like:b; num:n; value:yes; ty:ty > expr =
function
| Int _ as i -> i
| Float _ as f -> f
| If (cond,l,r) ->
let cond = match value cond with
| Int x -> x <> 0
| _ -> .
in
if cond then value l else value r
| Add (x,y) ->
match value x, value y with
| Int x, Int y -> Int (x+y)
| Float f, Float g -> Float (f+.g)
| _ -> .
```

Looking at the definition of `If`

, one might be tempted to simplify it to:

```
| If:
<bool_like:yes; .. > expr
* ...
```

since what matters is that the type is `bool_like`

. However, this is not equivalent simplification, and the previously correct `value`

cannot be typechecked anymore

```
| let cond = match value cond with
^^^^
```

```
Error: This expression has type < bool_like : yes; .. > expr
but an expression was expected of type
< bool_like : 'a; num : 'b; ty : 'c; value : 'd > expr
Type $a is not compatible with type < num : 'b; ty : 'c; value : 'd >
Hint: $a is an existential type bound by the constructor If.
```

The mysterious error message is due to the fact that in the new definition of `If`

```
| If:
<bool_like:yes; .. > expr
```

the row type variable is now an existentially quantified type variable. And such existentially quantified row type variable cannot be extended with fields anymore. Thus when we extract the `cond`

term in

```
| If (cond,l,r) ->
let cond = match value cond with
| Int x -> x <> 0
| _ -> .
in
if cond then value l else value r
```

we are not allowed to inspect any of the other type tag of the condition, which fails because `value`

require all type tags to be available.

In this case, we can recover the simplification by making sure that we don’t have an existentially quantified row variable with:

```
type 'a tagged = 'a
constraint 'a = <bool_like:'b; num:'n; value:'v; ty:'ty>
...
| If:
<bool_like:yes; .. > tagged expr
```

But this means that we are keeping away from any subtyping through row type variable.

In other words, subtyping through row type parametric polymorphism doesn’t work reliably in presence of GADTs, and my advice is to avoid it at the start rather than eliminate it later on after many fights against existentially quantified row type variables.