String.set conversion to OCaml 5

I have some old code that I want to compile on a newer system. I believe this code was written for OCaml version <= 4, and now I am trying to compile using OCaml 5.1.0 on an Apple M2 Max on MacOS 14.1.2.

let recapitalize string cap = 
  if string = "" then string else
  let _ = match cap with 
  | Capitalized -> string.[0] <- Char.uppercase string.[0]
  | Allcaps -> let len = String.length string in
    for i = 0 to len - 1; do 
      string.[i] <- Char.uppercase string.[i]
    done
  | Mixed l -> 
      let len = String.length string in
      List.iter (fun i -> if i < len then string.[i] <- Char.uppercase string.[i]) l
  | _ -> ()
  in
  string

I have made some updates myself, such as
Capitalized -> String.capitalize_ascii string

and
Allcaps -> String.uppercase_ascii string

I am currently stuck on trying to convert the Mixed function. I tried some of the suggestions from Warning when using `a.[b] <- c` operator with bytes, but I am still getting errors along the lines of “This expression has type unit but an expression was expected of type string” when I try Bytes.set. How can I properly convert this function to OCaml 5?

Here is a possible approach: you create a bytes value from your string, then modify the bytes according to l as before. And then convert the modified bytes back into a string and return that.

| Mixed l ->
    let buf = Bytes.of_string string in
    List.iter (fun i -> if i < len then Bytes.set buf i (Char.uppercase_ascii string.[i])) l;
    Bytes.to_string buf

Cheers,
Nicolas

You can type:

type cap_type = Capitalized | Allcaps | Mixed of int | NoChange

let recapitalize string cap =
  if string = "" then string else
  match cap with
  | Capitalized -> String.capitalize_ascii string
  | Allcaps -> String.uppercase_ascii string
  | Mixed l ->
      String.mapi
         (fun i c -> if i < l then Stdlib.Char.uppercase_ascii c
                              else c) string
  | _ -> string

let () = print_string (recapitalize "supercalifragilistic" Capitalized)
let () = print_newline ()
let () = print_string (recapitalize "supercalifragilistic" Allcaps)
let () = print_newline ()
let () = print_string (recapitalize "supercalifragilistic" (Mixed 5))
let () = print_newline ()

It is a pure functional version of your program. Note that String.capitalize_ascii string doesn’t change string. It only returns a capitalized version. It doesn’t behave like you want in your program.

Especially, you can’t mix in a match, string returns (String.capitalize_ascii string), and void return ().

The nojb approach can also be done, but its snipset doesn’t change string either but returns an altered version of string. You have to change the whole funtion which musn’t return string.

PS : the String functions uses internally some Bytes.unsafe_* functions which can be more efficient than if you use safe statements. (unsafe_of_string, unsafe_set for example). However, my function computes all characters even if only 2 are changed.