I must point out that “compiler libs” (the set of modules that are part of the compiler implementation) give currently no guarantee about the stability of their API. You should only use it if you really need it and you know what you are doing.
So for a simple function such as string_of_file that can be implemented independently from the compiler codebase, you really should use any other library (containers, bos, base) instead of compiler libs.
let string_of_file fn =
let buff_size = 1024 in
let buff = Buffer.create buff_size in
let ic = open_in fn in
let line_buff = Bytes.create buff_size in
let was_read = ref (input ic line_buff 0 buff_size) in
while !was_read <> 0 do
Buffer.add_subbytes buff line_buff 0 !was_read;
was_read := input ic line_buff 0 buff_size;
I do think the symmetric operation to output_string is called input_line in the stdlib:
val input_line in_channel -> string
Read characters from the given input channel, until a
newline character is encountered. Return the string of
all characters read, without the newline character at the end.
Raise [End_of_file] if the end of the file is reached
at the beginning of line.
There’s something like that in all and every stdlib extension or alternative, including batteries and containers! Oftentimes it’s better to load a file in its entirety, so it won’t change (e.g. if a source file won’t fit in memory, a compiler will have trouble processing it!)