While I obviously can't comment on the details of the course you took a decade ago, I do have to disagree vehemently with this. OCaml is close to as ideal a systems programming language as I've ever used since:
- it has a simple, stable and predictable runtime with strict evaluation semantics, so you can identify what statements allocate memory by inspecting code.
- the compilation to native code is fast and Unix-like, with standard object files generated that can be linked to and from C programs easily.
- the memory representation of values is uniform and native code debugging with gdb/lldb works great, thanks to the DWARF symbols emitted.
- OCaml's basic language features such as algebraic data types and exhaustive pattern matching are fantastic for both building low-level programs, and refactoring them in the future as they inevitably get more complex.
> not for simple tasks which are primarily IO and need to handle many cases.
And this is exactly where libraries like Async or Lwt are so useful, since you can juggle millions of concurrent threads and still do high-level programming without getting lost in the noise. Error monads, failure monitors, function wrappers to ensure resources get freed, can all be built directly within the language itself using the usual ML abstractions, and the compiled code is still lightweight and natively compiled.
For those interested in reading more about the runtime system in OCaml, the entire third part of Real World OCaml is dedicated to this topic: https://realworldocaml.org/v1/en/html/pt03.html