let sequence_id_of_string s =
    let open Result.Monad_infix in
    let i name value =
      try Ok (Int.of_string value)
      with Failure _ ->
        error (sprintf "%s not an int" name) value sexp_of_string
    in
    let b name value = match value with
      | "Y" -> Ok true
      | "N" -> Ok false
      | _ -> error (sprintf "%s must be Y or N" name) value sexp_of_string
    in
    match String.lsplit2 s ~on:' ' with
    | Some (x,y) -> (
      match String.split x ~on:':'String.split y ~on:':' with
      | [instrument;run_number;flowcell_id;lane;tile;x_pos;y_pos],
        [read;is_filtered;control_number;index] ->
        i "run_number" run_number >>= fun run_number ->
        i "lane" lane >>= fun lane ->
        tile_of_string tile >>= fun tile ->
        i "x_pos" x_pos >>= fun x_pos ->
        i "y_pos" y_pos >>= fun y_pos ->
        i "read" read >>= fun read ->
        b "is_filtered" is_filtered >>= fun is_filtered ->
        i "control_number" control_number >>= fun control_number ->
        Ok {instrument; run_number; flowcell_id; lane; tile; x_pos; y_pos;
            read; is_filtered; control_number; index}
      | _ -> error "invalid Illumina sequence identifier" s sexp_of_string
    )
    | _ -> error "invalid Illumina sequence identifier" s sexp_of_string