One option is to keep the full parity type available in the parity list:
type even = < even:yes; odd:no >
type odd = < even:no; yes:odd >
type (_,_) plist =
| []: (even,'elt) plist
| (::):
'elt * (<even: 'e; odd: 'o>, 'elt) plist ->
(<even: 'o; odd: 'e>, 'elt) plist
then cons is just
let cons x l = x :: l
and one can check that adding twice an element keep the same parity:
let cons_twice: type e o x.
x -> (<even:e;odd:o> as 'p, x) plist -> ('p, x) plist =
fun x l -> x :: x :: l
However, once the parity type has been projected to only the odd or even component, one needs to reintroduce the equation linking the odd and even types to switch the parity.
Note however that this not necessary if the parity stays the same. Reusing the definition
type odd = Odd
type even = Even
type ('previous, 'current) parity =
| O : (even, odd) parity
| E : (odd, even) parity
type (_, 'a) l =
| Nil : (even, 'a) l
| Cons : 'a * ('p, 'q) parity * ('p, 'a) l -> ('q, 'a) l
then adding twice an element do not require a parity argument:
let cons_twice: type x p. x -> (p,x) l -> (p,x) l =
fun x l ->
match l with
| Nil -> Cons(x,E, Cons(x,O, Nil))
| Cons(_,O,_) as r -> Cons(x,O, Cons(x, E, r))
| Cons(_,E,_) as r -> Cons(x,E, Cons(x, O, r))
Similarly, adding thrice an element requires to reintroduce the parity equation once
let switch: type e o. (e,o) parity -> (o,e) parity = function
| O -> E
| E -> O
let cons_thrice: type x p q. (p,q) parity -> x -> (p,x) l -> (q,x) l =
fun p x l ->
let p' = switch p in
Cons(x,p, Cons(x,p',Cons(x,p,l)))